├── .gitattributes ├── Client ├── Client.sln └── Client │ ├── ChatClient.cpp │ ├── ChatClient.h │ ├── Client.aps │ ├── Client.cpp │ ├── Client.h │ ├── Client.ico │ ├── Client.rc │ ├── Client.vcxproj │ ├── Client.vcxproj.filters │ ├── Definition.h │ ├── GroupChatBox.cpp │ ├── GroupChatBox.h │ ├── PrivateChatBox.cpp │ ├── PrivateChatBox.h │ ├── ReadMe.txt │ ├── ThreadFunc.cpp │ ├── ThreadFunc.h │ ├── resource.h │ ├── server.ini │ ├── sound.wav │ ├── stdafx.cpp │ ├── stdafx.h │ └── targetver.h ├── Demo ├── client1.jpg ├── client2.jpg ├── client3.jpg ├── client4.jpg ├── client5.jpg ├── client6.jpg └── server.jpg ├── Docs.pdf ├── README.md ├── Release ├── Client │ ├── Client.exe │ └── server.ini └── Server │ ├── Server.exe │ └── userdata.ini └── Server ├── Server.sln └── Server ├── ChatServer.cpp ├── ChatServer.h ├── Definition.h ├── ReadMe.txt ├── Server.aps ├── Server.cpp ├── Server.h ├── Server.ico ├── Server.rc ├── Server.vcxproj ├── Server.vcxproj.filters ├── ThreadFunc.cpp ├── ThreadFunc.h ├── resource.h ├── stdafx.cpp ├── stdafx.h └── targetver.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Client/Client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Client", "Client\Client.vcxproj", "{A55DB7D9-2882-4C8C-941F-E77602C2B648}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Debug|x64.ActiveCfg = Debug|x64 17 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Debug|x64.Build.0 = Debug|x64 18 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Debug|x86.Build.0 = Debug|Win32 20 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Release|x64.ActiveCfg = Release|x64 21 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Release|x64.Build.0 = Release|x64 22 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Release|x86.ActiveCfg = Release|Win32 23 | {A55DB7D9-2882-4C8C-941F-E77602C2B648}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Client/Client/ChatClient.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ChatClient.h" 3 | 4 | 5 | ChatClient::ChatClient() 6 | { 7 | _isConnected = false; 8 | 9 | } 10 | 11 | void ChatClient::init(string ipAddress, int port) 12 | { 13 | 14 | _serverIPAddress = ipAddress; 15 | _serverPort = port; 16 | struct hostent *hp; 17 | unsigned int addr; 18 | struct sockaddr_in server; 19 | 20 | 21 | WSADATA wsaData; 22 | 23 | int wsaret = WSAStartup(0x101, &wsaData); 24 | 25 | 26 | if (wsaret != 0) 27 | { 28 | return; 29 | } 30 | 31 | _connect = socket(AF_INET, SOCK_STREAM, 0); 32 | if (_connect == INVALID_SOCKET) 33 | return; 34 | 35 | addr = inet_addr(_serverIPAddress.c_str()); 36 | hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); 37 | 38 | if (hp == NULL) 39 | { 40 | closesocket(_connect); 41 | return; 42 | } 43 | 44 | server.sin_addr.s_addr = *((unsigned long*)hp->h_addr); 45 | server.sin_family = AF_INET; 46 | server.sin_port = htons(_serverPort); 47 | if(connect(_connect,(struct sockaddr*)&server,sizeof(server))) 48 | { 49 | closesocket(_connect); 50 | return; 51 | } 52 | _isConnected = true; 53 | return; 54 | } 55 | 56 | ChatClient::~ChatClient() 57 | { 58 | if(_isConnected) 59 | closesocket(_connect); 60 | } 61 | 62 | int ChatClient::sendMessagePort(WCHAR* message, int len) 63 | { 64 | int iStat = 0; 65 | 66 | iStat = send(_connect, (char*)message, len * 2 + 2, 0); 67 | if(iStat == -1) 68 | return 1; 69 | 70 | return 0; 71 | 72 | } 73 | 74 | int ChatClient::recMessagePort() 75 | { 76 | 77 | char acRetData[4096]; 78 | int iStat = 0; 79 | 80 | iStat = recv(_connect, acRetData, 4096, 0); 81 | if(iStat == -1) 82 | return 1; 83 | SendMessage(_hwnd, WM_COMMAND, (WPARAM)IDC_RECEIVE, (LPARAM)acRetData); 84 | 85 | return 0; 86 | 87 | } 88 | 89 | bool ChatClient::isConnected() 90 | { 91 | return _isConnected; 92 | } 93 | 94 | void ChatClient::setHWND(HWND hwnd) 95 | { 96 | _hwnd = hwnd; 97 | } 98 | 99 | void ChatClient::setUsername(wstring username) 100 | { 101 | _username = username; 102 | } 103 | 104 | wstring & ChatClient::getUsername() 105 | { 106 | return _username; 107 | } 108 | -------------------------------------------------------------------------------- /Client/Client/ChatClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _AFXDLL 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "resource.h" 10 | using namespace std; 11 | 12 | 13 | class ChatClient 14 | { 15 | public: 16 | ChatClient(); 17 | ~ChatClient(); 18 | void init(string ipAddress, int port); 19 | int sendMessagePort(WCHAR* message, int len); 20 | int recMessagePort(); 21 | bool isConnected(); 22 | void setHWND(HWND hwnd); 23 | void setUsername(wstring username); 24 | wstring& getUsername(); 25 | private: 26 | bool _isConnected; // true - connected false - not connected 27 | string _serverIPAddress; 28 | int _serverPort; 29 | SOCKET _connect; // socket connected to server 30 | HWND _hwnd; 31 | wstring _username; 32 | }; 33 | -------------------------------------------------------------------------------- /Client/Client/Client.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Client/Client/Client.aps -------------------------------------------------------------------------------- /Client/Client/Client.cpp: -------------------------------------------------------------------------------- 1 | // Client.cpp : Defines the entry point for the application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "Client.h" 6 | 7 | #define MAX_LOADSTRING 100 8 | 9 | // Global Variables: 10 | HINSTANCE hInst; // current instance 11 | WCHAR szTitle[MAX_LOADSTRING]; // The title bar text 12 | WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name 13 | 14 | // Forward declarations of functions included in this code module: 15 | ATOM MyRegisterClass(HINSTANCE hInstance); 16 | BOOL InitInstance(HINSTANCE, int); 17 | LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 18 | INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 19 | 20 | int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 21 | _In_opt_ HINSTANCE hPrevInstance, 22 | _In_ LPWSTR lpCmdLine, 23 | _In_ int nCmdShow) 24 | { 25 | UNREFERENCED_PARAMETER(hPrevInstance); 26 | UNREFERENCED_PARAMETER(lpCmdLine); 27 | 28 | // TODO: Place code here. 29 | 30 | // Initialize global strings 31 | LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 32 | LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING); 33 | // MyRegisterClass(hInstance); 34 | if (myRegClass(WndProc, szWindowClass, hInst) == false) 35 | return 0; 36 | 37 | if (myRegClass(ChatBoxProc, L"chatbox", hInst) == false) 38 | return 0; 39 | // Perform application initialization: 40 | if (!InitInstance (hInstance, nCmdShow)) 41 | { 42 | return FALSE; 43 | } 44 | 45 | HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT)); 46 | 47 | MSG msg; 48 | 49 | // Main message loop: 50 | while (GetMessage(&msg, nullptr, 0, 0)) 51 | { 52 | if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 53 | { 54 | TranslateMessage(&msg); 55 | DispatchMessage(&msg); 56 | } 57 | } 58 | 59 | return (int) msg.wParam; 60 | } 61 | 62 | bool myRegClass(WNDPROC lpfnWndProc, WCHAR *szClassName, HINSTANCE hInst) 63 | { 64 | WNDCLASSEX wincl; /* Data structure for the windowclass */ 65 | 66 | /* The Window structure */ 67 | wincl.hInstance = hInst; 68 | wincl.lpszClassName = szClassName; 69 | wincl.lpfnWndProc = lpfnWndProc; /* This function is called by windows */ 70 | wincl.style = CS_HREDRAW | CS_VREDRAW; /* Catch double-clicks */ 71 | wincl.cbSize = sizeof(WNDCLASSEX); 72 | 73 | /* Use default icon and mouse-pointer */ 74 | wincl.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_CLIENT)); 75 | wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 76 | wincl.hCursor = LoadCursor(NULL, IDC_ARROW); 77 | wincl.hbrBackground = (HBRUSH)(COLOR_BTNHILIGHT + 1); 78 | wincl.lpszMenuName = NULL; /* No menu */ 79 | wincl.cbClsExtra = 0; /* No extra bytes after the window class */ 80 | wincl.cbWndExtra = 0; /* structure or the window instance */ 81 | /* Use Windows's default colour as the background of the window */ 82 | wincl.hIconSm = LoadIcon(wincl.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 83 | 84 | /* Register the window class, and if it fails quit the program */ 85 | if (RegisterClassEx(&wincl)) 86 | return true; 87 | else 88 | return false; 89 | } 90 | 91 | // 92 | // FUNCTION: MyRegisterClass() 93 | // 94 | // PURPOSE: Registers the window class. 95 | // 96 | ATOM MyRegisterClass(HINSTANCE hInstance) 97 | { 98 | WNDCLASSEXW wcex; 99 | 100 | wcex.cbSize = sizeof(WNDCLASSEX); 101 | 102 | wcex.style = CS_HREDRAW | CS_VREDRAW; 103 | wcex.lpfnWndProc = WndProc; 104 | wcex.cbClsExtra = 0; 105 | wcex.cbWndExtra = 0; 106 | wcex.hInstance = hInstance; 107 | wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLIENT)); 108 | wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 109 | wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 110 | wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENT); 111 | wcex.lpszClassName = szWindowClass; 112 | wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 113 | 114 | return RegisterClassExW(&wcex); 115 | } 116 | 117 | // 118 | // FUNCTION: InitInstance(HINSTANCE, int) 119 | // 120 | // PURPOSE: Saves instance handle and creates main window 121 | // 122 | // COMMENTS: 123 | // 124 | // In this function, we save the instance handle in a global variable and 125 | // create and display the main program window. 126 | // 127 | BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 128 | { 129 | hInst = hInstance; // Store instance handle in our global variable 130 | 131 | HWND hWnd = CreateWindowW(szWindowClass, L"Chat Client", WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 132 | CW_USEDEFAULT, 0, 400, 600, nullptr, nullptr, hInstance, nullptr); 133 | 134 | if (!hWnd) 135 | { 136 | return FALSE; 137 | } 138 | 139 | ShowWindow(hWnd, nCmdShow); 140 | UpdateWindow(hWnd); 141 | 142 | return TRUE; 143 | } 144 | 145 | // 146 | // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 147 | // 148 | // PURPOSE: Processes messages for the main window. 149 | // 150 | // WM_COMMAND - process the application menu 151 | // WM_PAINT - Paint the main window 152 | // WM_DESTROY - post a quit message and return 153 | // 154 | // 155 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 156 | { 157 | switch (message) 158 | { 159 | HANDLE_MSG(hWnd, WM_CREATE, OnCreate); 160 | HANDLE_MSG(hWnd, WM_COMMAND, OnCommand); 161 | HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy); 162 | HANDLE_MSG(hWnd, WM_PAINT, OnPaint); 163 | HANDLE_MSG(hWnd, WM_DRAWITEM, OnDrawItem); 164 | case WM_CTLCOLORSTATIC: 165 | { 166 | HWND hStatic = (HWND)lParam; 167 | HDC hdc = (HDC)wParam; 168 | 169 | //SetTextColor(hdc, RGB(0, 215, 194)); 170 | SetBkMode(hdc, TRANSPARENT); 171 | //return (LRESULT)CreateSolidBrush(RGB(255, 255, 255)); 172 | return (LRESULT)GetStockObject(DC_BRUSH); 173 | } 174 | } 175 | return DefWindowProc(hWnd, message, wParam, lParam); 176 | } 177 | 178 | void OnDestroy(HWND hwnd) 179 | { 180 | GdiplusShutdown(gdiplusToken); 181 | PostQuitMessage(0); 182 | } 183 | 184 | BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) 185 | { 186 | INITCOMMONCONTROLSEX icex; 187 | 188 | icex.dwSize = sizeof(icex); 189 | icex.dwICC = ICC_DATE_CLASSES; 190 | 191 | InitCommonControlsEx(&icex); 192 | GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 193 | 194 | char buf[4096]; 195 | gCurScene = 0; 196 | 197 | FILE *fp = fopen("server.ini", "r"); 198 | if (fp == NULL) 199 | { 200 | MessageBox(0, L"\nUnable to open server.ini. Please specify server IPsddress in server.ini", 0, 0); 201 | return 1; 202 | } 203 | string sServerAddress; 204 | while ((fgets(buf, 4096, fp)) != NULL) 205 | { 206 | if (buf[0] == '#') 207 | continue; 208 | sServerAddress = buf; 209 | 210 | } 211 | fclose(fp); 212 | 213 | if (sServerAddress.size() == 0) 214 | { 215 | MessageBox(hWnd, L"\nUnable to connect to the IP address specified in server.ini\r\nPlease check server IP address.", 0, 0); 216 | return 0; 217 | } 218 | 219 | gClientObj.init(sServerAddress.c_str(), 8084); 220 | if (!gClientObj.isConnected()) 221 | { 222 | MessageBox(hWnd, L"\nUnable to connect to the IP address specified in server.ini\r\nPlease check server IP address.", 0, 0); 223 | return 0; 224 | } 225 | gClientObj.setHWND(hWnd); 226 | AfxBeginThread(recMessageThread, 0); 227 | 228 | 229 | hFont = CreateFont(17, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, 230 | OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 231 | DEFAULT_PITCH | FF_DONTCARE, L"Arial"); 232 | 233 | if (hFont == NULL) 234 | { 235 | LOGFONT lf; 236 | GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); 237 | CreateFont(lf.lfHeight*1.6, lf.lfWidth*1.6, 238 | lf.lfEscapement, lf.lfOrientation, lf.lfWeight, 239 | lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet, 240 | lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality, 241 | lf.lfPitchAndFamily, lf.lfFaceName); 242 | } 243 | SetWindowFont(hwnd, hFont, true); 244 | hUsername = CreateWindowEx(0, L"edit", L"", WS_VISIBLE | WS_CHILD, 50, 160, 280, 35, hWnd, 0, hInst, 0); 245 | SetWindowFont(hUsername, hFont, true); 246 | hPassword = CreateWindowEx(0, L"edit", L"", WS_VISIBLE | WS_CHILD | ES_PASSWORD, 50, 250, 280, 35, hWnd, (HMENU)99, hInst, 0); 247 | SetWindowFont(hPassword, hFont, true); 248 | hSignUp = CreateWindowEx(0, L"button", L"Sign Up", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 50, 320, 135, 30, hWnd, (HMENU)IDC_SIGNUP, hInst, 0); 249 | hLogIn = CreateWindowEx(0, L"button", L"Log In", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 195, 320, 135, 30, hWnd, (HMENU)IDC_LOGIN, hInst, 0); 250 | } 251 | 252 | void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) 253 | { 254 | switch (id) 255 | { 256 | case IDM_ABOUT: 257 | DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 258 | break; 259 | case IDM_EXIT: 260 | DestroyWindow(hWnd); 261 | break; 262 | case IDC_RECEIVE: 263 | { 264 | WCHAR* message = (WCHAR*)hwndCtl; 265 | switch (message[0]) 266 | { 267 | case MessageType::PRIVATE_CHAT: 268 | { 269 | /* 270 | * receive: message = [FLAG | receiver | NULL | sender | NULL | content | NULL] 271 | */ 272 | PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC); 273 | message++; 274 | int len = wcslen(message); 275 | WCHAR* partner; 276 | WCHAR* content; 277 | 278 | partner = message + len + 1; 279 | content = partner + wcslen(partner) + 1; 280 | 281 | for (auto chatbox : gPrivateChatBoxList) 282 | { 283 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 284 | { 285 | chatbox->receiveMessage(content); 286 | SetForegroundWindow(chatbox->getHWND()); 287 | SetActiveWindow(chatbox->getHWND()); 288 | return; 289 | } 290 | } 291 | auto cb = PrivateChatBox::create(hWnd, hInst, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(500, 500), partner); 292 | cb->setFont(hFont); 293 | cb->setUsername(gClientObj.getUsername()); 294 | gPrivateChatBoxList.push_back(cb); 295 | cb->receiveMessage(content); 296 | SetForegroundWindow(cb->getHWND()); 297 | SetActiveWindow(cb->getHWND()); 298 | break; 299 | } 300 | case MessageType::GROUP_CHAT: 301 | { 302 | /* 303 | * receive: message = message = [FLAG | group name | NULL | sender | NULL | content | NULL] 304 | */ 305 | PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC); 306 | message++; 307 | int len = wcslen(message); 308 | WCHAR buffer[10000]; 309 | WCHAR* groupname; 310 | WCHAR* sendername; 311 | WCHAR* content; 312 | groupname = message; 313 | sendername = message + len + 1; 314 | content = sendername + wcslen(sendername) + 1; 315 | 316 | wcscpy(buffer, L"$["); 317 | wcscat(buffer, sendername); 318 | wcscat(buffer, L"]:\r\n"); 319 | wcscat(buffer, content); 320 | for (auto chatbox : gGroupChatBoxList) 321 | { 322 | if (wcscmp(chatbox->getGroupName().c_str(), groupname) == 0) 323 | { 324 | chatbox->receiveMessage(buffer); 325 | SetForegroundWindow(chatbox->getHWND()); 326 | SetActiveWindow(chatbox->getHWND()); 327 | return; 328 | } 329 | } 330 | auto cb = GroupChatBox::create(hWnd, hInst, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(500, 500), groupname); 331 | cb->setFont(hFont); 332 | gGroupChatBoxList.push_back(cb); 333 | cb->receiveMessage(buffer); 334 | cb->setUsername(gClientObj.getUsername()); 335 | SetForegroundWindow(cb->getHWND()); 336 | SetActiveWindow(cb->getHWND()); 337 | break; 338 | } 339 | case MessageType::END_PRIVATE_CHAT: 340 | { 341 | /* 342 | * receive: message = [FLAG | receiver | NULL | sender | NULL] 343 | */ 344 | message++; 345 | WCHAR* sender; 346 | sender = message + wcslen(message) + 1; 347 | 348 | for (auto chatbox : gPrivateChatBoxList) 349 | { 350 | if (wcscmp(chatbox->getPartner().c_str(), sender) == 0) 351 | { 352 | chatbox->onEndChat(); 353 | break; 354 | } 355 | } 356 | break; 357 | } 358 | case MessageType::END_GROUP_CHAT: 359 | { 360 | /* 361 | * receive: message = [FLAG | group name | NULL | sender | NULL] 362 | */ 363 | message++; 364 | int len = wcslen(message); 365 | WCHAR* sender; 366 | sender = message + len + 1; 367 | for (auto chatbox : gGroupChatBoxList) 368 | { 369 | if (wcscmp(chatbox->getGroupName().c_str(), message) == 0) 370 | { 371 | chatbox->onUserLeft(sender); 372 | break; 373 | } 374 | } 375 | break; 376 | } 377 | case MessageType::SU_SUCCESS: 378 | { 379 | /* 380 | * receive: message = [FLAG] 381 | */ 382 | MessageBox(hWnd, L"Sign Up Successfull!", L"Congratulations", 0); 383 | break; 384 | } 385 | case MessageType::SU_FAILURE: 386 | { 387 | /* 388 | * receive: message = [FLAG] 389 | */ 390 | MessageBox(hWnd, L"Sign Up Failed", 0, 0); 391 | break; 392 | } 393 | case MessageType::LI_SUCCESS: 394 | { 395 | /* 396 | * receive: message = [FLAG] 397 | */ 398 | MessageBox(hWnd, L"Log In Successful!", L"Congratulations", 0); 399 | DestroyWindow(hUsername); 400 | DestroyWindow(hPassword); 401 | DestroyWindow(hSignUp); 402 | DestroyWindow(hLogIn); 403 | gCurScene = 1; 404 | InvalidateRect(hWnd, 0, TRUE); 405 | 406 | hInvitedUsername = CreateWindowEx(0, L"edit", L"", WS_VISIBLE | WS_CHILD, 50, 190, 280, 35, hWnd, 0, hInst, 0); 407 | SetWindowFont(hInvitedUsername, hFont, true); 408 | hPrivateChat = CreateWindowEx(0, L"button", L"Private Chat", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 50, 120, 280, 35, hWnd, (HMENU)IDC_PRIVATECHAT, hInst, 0); 409 | SetWindowFont(hPrivateChat, hFont, true); 410 | hGroupName = CreateWindowEx(0, L"edit", L"", WS_VISIBLE | WS_CHILD, 50, 330, 280, 35, hWnd, 0, hInst, 0); 411 | SetWindowFont(hGroupName, hFont, true); 412 | hGroupChat = CreateWindowEx(0, L"button", L"Group Chat", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 50, 260, 280, 35, hWnd, (HMENU)IDC_GROUPCHAT, hInst, 0); 413 | SetWindowFont(hGroupChat, hFont, true); 414 | hCreate = CreateWindowEx(0, L"button", L"Create", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 50, 400, 280, 35, hWnd, (HMENU)IDC_CREATE, hInst, 0); 415 | SendMessage(hPrivateChat, BM_SETCHECK, BST_CHECKED, 0); 416 | break; 417 | } 418 | case MessageType::LI_FAILURE: 419 | { 420 | /* 421 | * receive: message = [FLAG] 422 | */ 423 | MessageBox(hWnd, L"Username or password is incorrect.", 0, 0); 424 | break; 425 | } 426 | case MessageType::C_PC_FAILURE: 427 | { 428 | /* 429 | * receive: message = [FLAG] 430 | */ 431 | MessageBox(hWnd, L"The user is offline.", L"Create private chat", 0); 432 | break; 433 | } 434 | case MessageType::C_PC_SUCCESS: 435 | { 436 | /* 437 | * receive: message = [FLAG | partner | NULL] 438 | */ 439 | message++; 440 | int len = wcslen(message); 441 | MessageBox(hWnd, L"The user is online.", L"Create private chat", 0); 442 | for (auto chatbox : gPrivateChatBoxList) 443 | { 444 | if (wcscmp(chatbox->getPartner().c_str(), message) == 0) 445 | { 446 | return; 447 | } 448 | } 449 | auto cb = PrivateChatBox::create(hWnd, hInst, Point(100, 100), Size(500, 500), message); 450 | cb->setFont(hFont); 451 | cb->setUsername(gClientObj.getUsername()); 452 | gPrivateChatBoxList.push_back(cb); 453 | break; 454 | } 455 | case MessageType::C_GC_FAILURE: 456 | { 457 | /* 458 | * receive: message = [FLAG] 459 | */ 460 | MessageBox(hWnd, L"You created a group chat by this name. Please use another group's name!", L"Create group chat", 0); 461 | break; 462 | } 463 | case MessageType::C_GC_SUCCESS: 464 | { 465 | /* 466 | * receive: message = [FLAG | group name | NULL] 467 | */ 468 | message++; 469 | int len = wcslen(message); 470 | MessageBox(0, L"The group chat was created.", L"Create group chat", 0); 471 | for (auto chatbox : gGroupChatBoxList) 472 | { 473 | if (wcscmp(chatbox->getGroupName().c_str(), message) == 0) 474 | { 475 | return; 476 | } 477 | } 478 | auto cb = GroupChatBox::create(hWnd, hInst, Point(100, 100), Size(500, 500), message); 479 | cb->setFont(hFont); 480 | cb->setUsername(gClientObj.getUsername()); 481 | gGroupChatBoxList.push_back(cb); 482 | break; 483 | } 484 | case MessageType::GC_AU_FAILURE: 485 | { 486 | /* 487 | * receive: message = [FLAG | group name | NULL] 488 | */ 489 | MessageBox(hWnd, L"The user was offline or has been added to the group.", L"Add user", 0); 490 | break; 491 | } 492 | case MessageType::GC_AU_SUCCESS: 493 | { 494 | /* 495 | * receive: message = [FLAG | group name | NULL | added user | NULL] 496 | */ 497 | message++; 498 | int len = wcslen(message); 499 | WCHAR* username; 500 | username = message + len + 1; 501 | for (auto group : gGroupChatBoxList) 502 | { 503 | if (wcscmp(group->getGroupName().c_str(), message) == 0) 504 | { 505 | group->onUserJoin(username); 506 | } 507 | } 508 | //MessageBox(hWnd, L"Adding user to the group successfully.", 0, 0); 509 | break; 510 | } 511 | case MessageType::SEND_FILE: 512 | { 513 | /* 514 | * receive: message = [FLAG | file name | NULL | file size | NULL | receiver | NULL | sender | NULL] 515 | * send: message = [FLAG | file name | NULL | file size | NULL | receiver | NULL | sender | NULL] 516 | */ 517 | message++; 518 | int len = wcslen(message); 519 | WCHAR* partner; 520 | DWORD size; 521 | len++; 522 | size = *(message + len) << 16; 523 | len++; 524 | size += *(message + len); 525 | len += 2; 526 | partner = message + len; 527 | int len2 = wcslen(partner); 528 | len += len2 + 1; 529 | partner += len2 + 1; 530 | PrivateChatBox* prChat = NULL; 531 | for (auto chatbox : gPrivateChatBoxList) 532 | { 533 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 534 | { 535 | prChat = chatbox; 536 | break; 537 | } 538 | } 539 | 540 | if (!prChat) 541 | { 542 | prChat = PrivateChatBox::create(hWnd, hInst, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(500, 500), partner); 543 | prChat->setFont(hFont); 544 | prChat->setUsername(gClientObj.getUsername()); 545 | gPrivateChatBoxList.push_back(prChat); 546 | } 547 | WCHAR text[1000]; 548 | WCHAR* pSize = convertSize(size); 549 | wsprintf(text, L"%lS send to you a file:\nFile name: %lS\nSize: %lS\nDo you want to receive it?", partner, message, pSize); 550 | int result = MessageBox(prChat->getHWND(), text, L"File", MB_YESNO); 551 | 552 | message--; 553 | len += prChat->getPartner().size() + 1; 554 | if (result == IDYES) 555 | { 556 | message[0] = SF_ACCEPT; 557 | } 558 | else 559 | { 560 | message[0] = SF_CANCEL; 561 | gClientObj.sendMessagePort(message, len); 562 | break; 563 | } 564 | 565 | 566 | WCHAR buffer[1000]; 567 | wcscpy(buffer, message + 1); 568 | bool result2 = myCreateSaveFile(prChat->getHWND(), buffer); 569 | if (!result2) 570 | { 571 | message[0] = SF_CANCEL; 572 | gClientObj.sendMessagePort(message, len); 573 | break; 574 | } 575 | 576 | prChat->preReceiveFile(buffer, size); 577 | SetForegroundWindow(prChat->getHWND()); 578 | SetActiveWindow(prChat->getHWND()); 579 | gClientObj.sendMessagePort(message, len); 580 | break; 581 | } 582 | case MessageType::SF_CANCEL: 583 | { 584 | /* 585 | * receive: message = [FLAG | file name | NULL | file size | NULL | sender | NULL| receiver | NULL] 586 | */ 587 | message++; 588 | int len = wcslen(message); 589 | WCHAR* partner; 590 | 591 | partner = message + len + 4; 592 | for (auto chatbox : gPrivateChatBoxList) 593 | { 594 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 595 | { 596 | chatbox->onRefuseReceiveFile(); 597 | break; 598 | } 599 | } 600 | break; 601 | } 602 | case MessageType::SF_ACCEPT: 603 | { 604 | /* 605 | * receive: message = [FLAG | file name | NULL | file size | NULL | partner | NULL] 606 | * send: message = [FLAG | file name | NULL | file size | NULL | partner | NULL] 607 | */ 608 | 609 | message++; 610 | int len = wcslen(message); 611 | WCHAR* partner; 612 | 613 | partner = message + len + 4; 614 | for (auto chatbox : gPrivateChatBoxList) 615 | { 616 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 617 | { 618 | chatbox->onAcceptReceiveFile(); 619 | WCHAR buffer[1000]; 620 | int len = chatbox->onSendFile(buffer); 621 | 622 | gClientObj.sendMessagePort(buffer, len); 623 | break; 624 | } 625 | } 626 | break; 627 | } 628 | case MessageType::FILE_DATA: 629 | { 630 | /* 631 | * receive: message = [FLAG | file size | NULL | receiver | NULL | sender | NULL | content] 632 | */ 633 | message++; 634 | WCHAR* partner; 635 | WCHAR* content; 636 | DWORD size = *message; 637 | int len = 4 + gClientObj.getUsername().size(); 638 | partner = message + len; 639 | 640 | for (auto chatbox : gPrivateChatBoxList) 641 | { 642 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 643 | { 644 | content = message + len + chatbox->getPartner().size() + 2; 645 | bool result = chatbox->receiveFile(content, size); 646 | 647 | WCHAR messageReply[100]; 648 | WCHAR* sender; 649 | messageReply[0] = MessageType::CONTINUE; 650 | wcscpy(messageReply + 1, chatbox->getPartner().c_str()); 651 | sender = messageReply + chatbox->getPartner().size() + 2; 652 | wcscpy(sender, chatbox->getUsername().c_str()); 653 | 654 | int len = chatbox->getPartner().size() + chatbox->getUsername().size() + 3; 655 | 656 | if (!result) 657 | { 658 | messageReply[0] = MessageType::STOP; 659 | } 660 | gClientObj.sendMessagePort(messageReply, len); 661 | 662 | break; 663 | } 664 | } 665 | break; 666 | } 667 | case MessageType::CONTINUE: 668 | { 669 | /* 670 | * receive: message = [FLAG | receiver | NULL | sender | NULL] 671 | */ 672 | WCHAR* partner; 673 | partner = message + wcslen(message) + 1; 674 | for (auto chatbox : gPrivateChatBoxList) 675 | { 676 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 677 | { 678 | WCHAR buffer[600]; 679 | int len = chatbox->onSendFile(buffer); 680 | gClientObj.sendMessagePort(buffer, len); 681 | } 682 | } 683 | break; 684 | } 685 | case MessageType::STOP: 686 | { 687 | /* 688 | * receive: message = [FLAG | receiver | NULL | sender | NULL] 689 | */ 690 | WCHAR* partner; 691 | partner = message + wcslen(message) + 1; 692 | for (auto chatbox : gPrivateChatBoxList) 693 | { 694 | if (wcscmp(chatbox->getPartner().c_str(), partner) == 0) 695 | { 696 | chatbox->onStop(); 697 | } 698 | } 699 | break; 700 | } 701 | break; 702 | } 703 | break; 704 | } 705 | case IDC_SIGNUP: 706 | { 707 | /* 708 | * send: message = [FLAG | username | NULL | password | NULL] 709 | */ 710 | WCHAR username[50]; 711 | WCHAR password[50]; 712 | WCHAR message[100]; 713 | message[0] = MessageType::SIGNUP; 714 | message[1] = NULL; 715 | GetWindowText(hUsername, username, 50); 716 | if (username[0] == NULL) 717 | { 718 | MessageBox(hWnd, L"Username is empty.", 0, 0); 719 | return; 720 | } 721 | for (int i = 0; username[i] != NULL; i++) 722 | { 723 | if (username[i] == L';') 724 | { 725 | MessageBox(hWnd, L"The username has invalid characters: ;", 0, 0); 726 | return; 727 | } 728 | } 729 | GetWindowText(hPassword, password, 50); 730 | if (password[0] == NULL) 731 | { 732 | MessageBox(hWnd, L"Password is empty.", 0, 0); 733 | return; 734 | } 735 | wcscat(message, username); 736 | int len = wcslen(message); 737 | len++; 738 | int i; 739 | for (i = 0; password[i] != NULL; i++) 740 | { 741 | message[len + i] = password[i]; 742 | } 743 | len += i; 744 | message[len] = NULL; 745 | gClientObj.sendMessagePort(message, len); 746 | break; 747 | } 748 | case IDC_LOGIN: 749 | { 750 | /* 751 | * send: message = [FLAG | username | NULL | password | NULL] 752 | */ 753 | WCHAR username[50]; 754 | WCHAR password[50]; 755 | WCHAR message[100]; 756 | message[0] = MessageType::LOGIN; 757 | message[1] = NULL; 758 | GetWindowText(hUsername, username, 50); 759 | if (username[0] == NULL) 760 | { 761 | MessageBox(hWnd, L"Uername is empty.", 0, 0); 762 | return; 763 | } 764 | for (int i = 0; username[i] != NULL; i++) 765 | { 766 | if (username[i] == L';') 767 | { 768 | MessageBox(hWnd, L"Username has an invalid character: ;", 0, 0); 769 | return; 770 | } 771 | } 772 | GetWindowText(hPassword, password, 50); 773 | if (password[0] == NULL) 774 | { 775 | MessageBox(hWnd, L"Password is empty.", 0, 0); 776 | return; 777 | } 778 | wcscat(message, username); 779 | int len = wcslen(message); 780 | len++; 781 | int i; 782 | for (i = 0; password[i] != NULL; i++) 783 | { 784 | message[len + i] = password[i]; 785 | } 786 | len += i; 787 | message[len] = NULL; 788 | gClientObj.sendMessagePort(message, len); 789 | gClientObj.setUsername(username); 790 | break; 791 | } 792 | case IDC_CREATE: 793 | { 794 | if (IsDlgButtonChecked(hWnd, IDC_PRIVATECHAT)) 795 | { 796 | /* 797 | * send: message = [FLAG | partner | NULL] 798 | */ 799 | WCHAR message[50]; 800 | WCHAR buffer[50]; 801 | 802 | message[0] = MessageType::CREATE_PRIVATE_CHAT; 803 | message[1] = NULL; 804 | GetWindowText(hInvitedUsername, buffer, 50); 805 | if (buffer[0] == NULL) 806 | { 807 | MessageBox(hWnd, L"Username is empty!", 0, 0); 808 | return; 809 | } 810 | if (wcscmp(buffer, gClientObj.getUsername().c_str()) == 0) 811 | { 812 | MessageBox(hWnd, L"You can't create the private chat by your username!", 0, 0); 813 | return; 814 | } 815 | wcscat(message, buffer); 816 | int len = wcslen(message); 817 | gClientObj.sendMessagePort(message, len); 818 | SetWindowText(hInvitedUsername, L""); 819 | } 820 | else 821 | { 822 | /* 823 | * send: message = [FLAG | group name | NULL] 824 | */ 825 | WCHAR message[50]; 826 | WCHAR buffer[50]; 827 | message[0] = MessageType::CREATE_GROUP_CHAT; 828 | GetWindowText(hGroupName, buffer, 50); 829 | if (buffer[0] == NULL) 830 | { 831 | MessageBox(hWnd, L"Group name is empty!", 0, 0); 832 | return; 833 | } 834 | wcscpy(message + 1, buffer); 835 | int len = wcslen(message); 836 | gClientObj.sendMessagePort(message, len); 837 | SetWindowText(hGroupName, L""); 838 | } 839 | break; 840 | } 841 | } 842 | } 843 | void OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem) 844 | { 845 | switch (lpDrawItem->CtlID) 846 | { 847 | case IDC_SIGNUP: 848 | { 849 | auto graphics = new Graphics(lpDrawItem->hDC); 850 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 254, 88, 136)); 851 | graphics->FillRectangle(&brush, 0, 0, 135, 30); 852 | 853 | Gdiplus::FontFamily fontFamily(L"Arial"); 854 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 855 | Gdiplus::PointF pointF(35, 6); 856 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 857 | 858 | graphics->DrawString(L"Sign Up", -1, &font, pointF, &solidBrush); 859 | 860 | if (graphics) 861 | delete graphics; 862 | break; 863 | } 864 | case IDC_LOGIN: 865 | { 866 | auto graphics = new Graphics(lpDrawItem->hDC); 867 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 69, 215, 194)); 868 | graphics->FillRectangle(&brush, 0, 0, 135, 30); 869 | 870 | Gdiplus::FontFamily fontFamily(L"Arial"); 871 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 872 | Gdiplus::PointF pointF(43, 6); 873 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 874 | graphics->DrawString(L"Log In", -1, &font, pointF, &solidBrush); 875 | 876 | if (graphics) 877 | delete graphics; 878 | break; 879 | } 880 | case IDC_CREATE: 881 | { 882 | auto graphics = new Graphics(lpDrawItem->hDC); 883 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 69, 215, 194)); 884 | graphics->FillRectangle(&brush, 0, 0, 280, 35); 885 | 886 | Gdiplus::FontFamily fontFamily(L"Arial"); 887 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 888 | Gdiplus::PointF pointF(110, 8); 889 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 890 | graphics->DrawString(L"Create", -1, &font, pointF, &solidBrush); 891 | 892 | if (graphics) 893 | delete graphics; 894 | break; 895 | } 896 | } 897 | } 898 | 899 | void OnPaint(HWND hWnd) 900 | { 901 | PAINTSTRUCT ps; 902 | HDC hdc = BeginPaint(hWnd, &ps); 903 | 904 | auto graphics = new Graphics(hdc); 905 | Gdiplus::Pen pen(Gdiplus::Color(255, 37, 156, 236)); 906 | switch (gCurScene) 907 | { 908 | case 0: 909 | { 910 | graphics->DrawRectangle(&pen, 49, 159, 282, 37); 911 | graphics->DrawRectangle(&pen, 49, 249, 282, 37); 912 | 913 | Gdiplus::FontFamily fontFamily(L"Arial"); 914 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 915 | Gdiplus::PointF pointF(46.0f, 135.0f); 916 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 37, 156, 236)); 917 | 918 | graphics->DrawString(L"Username:", -1, &font, pointF, &solidBrush); 919 | 920 | pointF = Gdiplus::PointF(46.0f, 225.0f); 921 | graphics->DrawString(L"Password:", -1, &font, pointF, &solidBrush); 922 | break; 923 | } 924 | case 1: 925 | { 926 | Gdiplus::FontFamily fontFamily(L"Arial"); 927 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 928 | Gdiplus::PointF pointF(46.0f, 165.0f); 929 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 171, 210, 237)); 930 | graphics->DrawString(L"Username:", -1, &font, pointF, &solidBrush); 931 | graphics->DrawRectangle(&pen, 49, 189, 282, 37); 932 | 933 | pointF = Gdiplus::PointF(46.0f, 305.0f); 934 | graphics->DrawString(L"Group name:", -1, &font, pointF, &solidBrush); 935 | graphics->DrawRectangle(&pen, 49, 329, 282, 37); 936 | break; 937 | } 938 | } 939 | 940 | EndPaint(hWnd, &ps); 941 | } 942 | 943 | // Message handler for about box. 944 | INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 945 | { 946 | UNREFERENCED_PARAMETER(lParam); 947 | switch (message) 948 | { 949 | case WM_INITDIALOG: 950 | return (INT_PTR)TRUE; 951 | 952 | case WM_COMMAND: 953 | if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 954 | { 955 | EndDialog(hDlg, LOWORD(wParam)); 956 | return (INT_PTR)TRUE; 957 | } 958 | break; 959 | } 960 | return (INT_PTR)FALSE; 961 | } 962 | 963 | 964 | LRESULT CALLBACK ChatBoxProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 965 | { 966 | switch (message) /* handle the messages */ 967 | { 968 | case WM_CTLCOLORSTATIC: 969 | { 970 | HWND hStatic = (HWND)lParam; 971 | HDC hdc = (HDC)wParam; 972 | 973 | SetBkMode(hdc, TRANSPARENT); 974 | return (LRESULT)GetStockObject(DC_BRUSH); 975 | } 976 | case WM_DRAWITEM: 977 | { 978 | auto dit = (DRAWITEMSTRUCT *)(lParam); 979 | switch (dit->CtlID) 980 | { 981 | case IDC_SEND_GROUP: 982 | case IDC_SEND: 983 | { 984 | auto graphics = new Graphics(dit->hDC); 985 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 69, 215, 194)); 986 | graphics->FillRectangle(&brush, 0, 0, 75, 75); 987 | 988 | Gdiplus::FontFamily fontFamily(L"Arial"); 989 | Gdiplus::Font font(&fontFamily, 17, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 990 | Gdiplus::PointF pointF(15, 30); 991 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 992 | 993 | graphics->DrawString(L"Send", -1, &font, pointF, &solidBrush); 994 | 995 | if (graphics) 996 | delete graphics; 997 | break; 998 | } 999 | case IDC_ATTACH: 1000 | { 1001 | auto graphics = new Graphics(dit->hDC); 1002 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 160, 160, 160)); 1003 | graphics->FillRectangle(&brush, 0, 0, 75, 75); 1004 | 1005 | Gdiplus::FontFamily fontFamily(L"Arial"); 1006 | Gdiplus::Font font(&fontFamily, 17, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 1007 | Gdiplus::PointF pointF(10, 30); 1008 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 1009 | 1010 | graphics->DrawString(L"Attach", -1, &font, pointF, &solidBrush); 1011 | 1012 | if (graphics) 1013 | delete graphics; 1014 | break; 1015 | } 1016 | case IDC_ADD: 1017 | { 1018 | auto graphics = new Graphics(dit->hDC); 1019 | Gdiplus::SolidBrush brush(Gdiplus::Color(255, 160, 160, 160)); 1020 | graphics->FillRectangle(&brush, 0, 0, 75, 25); 1021 | 1022 | Gdiplus::FontFamily fontFamily(L"Arial"); 1023 | Gdiplus::Font font(&fontFamily, 15, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); 1024 | Gdiplus::PointF pointF(20, 3); 1025 | Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 255, 255, 255)); 1026 | 1027 | graphics->DrawString(L"Add", -1, &font, pointF, &solidBrush); 1028 | 1029 | if (graphics) 1030 | delete graphics; 1031 | break; 1032 | } 1033 | } 1034 | break; 1035 | } 1036 | case WM_CREATE: 1037 | break; 1038 | 1039 | case WM_DESTROY: 1040 | /* 1041 | * send: message = [FLAG | receiver | NULL | sender | NULL] 1042 | */ 1043 | for (auto chatbox : gPrivateChatBoxList) 1044 | { 1045 | if (chatbox->getHWND() == hwnd) 1046 | { 1047 | gPrivateChatBoxList.remove(chatbox); 1048 | WCHAR message[101]; 1049 | WCHAR* sender; 1050 | message[0] = MessageType::END_PRIVATE_CHAT; 1051 | message[1] = NULL; 1052 | wcscat(message, chatbox->getPartner().c_str()); 1053 | sender = message + chatbox->getPartner().size() + 2; 1054 | wcscpy(sender, gClientObj.getUsername().c_str()); 1055 | int len = chatbox->getPartner().size() + gClientObj.getUsername().size() + 3; 1056 | gClientObj.sendMessagePort(message, len); 1057 | break; 1058 | } 1059 | } 1060 | 1061 | /* 1062 | * send: message = [FLAG | group name | NULL | sender | NULL] 1063 | */ 1064 | for (auto chatbox : gGroupChatBoxList) 1065 | { 1066 | if (chatbox->getHWND() == hwnd) 1067 | { 1068 | gGroupChatBoxList.remove(chatbox); 1069 | WCHAR message[101]; 1070 | WCHAR* sender; 1071 | message[0] = MessageType::END_GROUP_CHAT; 1072 | message[1] = NULL; 1073 | wcscat(message, chatbox->getGroupName().c_str()); 1074 | 1075 | sender = message + chatbox->getGroupName().size() + 2; 1076 | wcscpy(sender, gClientObj.getUsername().c_str()); 1077 | int len = chatbox->getGroupName().size() + gClientObj.getUsername().size() + 3; 1078 | gClientObj.sendMessagePort(message, len); 1079 | break; 1080 | } 1081 | } 1082 | DestroyWindow(hwnd); 1083 | 1084 | break; 1085 | case WM_COMMAND: 1086 | switch (LOWORD(wParam)) 1087 | { 1088 | case IDC_SEND: 1089 | { 1090 | /* 1091 | * send: message = [FLAG | receiver | NULL | sender | NULL | content | NULL] 1092 | */ 1093 | for (auto chatbox : gPrivateChatBoxList) 1094 | { 1095 | if (chatbox->getHWND() == hwnd) 1096 | { 1097 | WCHAR buffer[1000]; 1098 | int len = chatbox->onPressBtnSend(buffer); 1099 | if (len == -1) 1100 | { 1101 | return 0; 1102 | } 1103 | gClientObj.sendMessagePort(buffer, len); 1104 | break; 1105 | } 1106 | } 1107 | break; 1108 | } 1109 | case IDC_SEND_GROUP: 1110 | { 1111 | /* 1112 | * send: message = [FLAG | group name | NULL | sender | NULL | content | NULL] 1113 | */ 1114 | for (auto chatbox : gGroupChatBoxList) 1115 | { 1116 | if (chatbox->getHWND() == hwnd) 1117 | { 1118 | WCHAR buffer[1000]; 1119 | int len = chatbox->onPressBtnSend(buffer); 1120 | if (len == -1) 1121 | { 1122 | return 0; 1123 | } 1124 | gClientObj.sendMessagePort(buffer, len); 1125 | break; 1126 | } 1127 | } 1128 | break; 1129 | } 1130 | case IDC_ADD: 1131 | { 1132 | /* 1133 | * send: message = [FLAG | group name | NULL | added user | NULL] 1134 | */ 1135 | for (auto chatbox : gGroupChatBoxList) 1136 | { 1137 | if (chatbox->getHWND() == hwnd) 1138 | { 1139 | WCHAR buffer[1000]; 1140 | int len = chatbox->onPressBtnAdd(buffer); 1141 | if (len == -1) 1142 | { 1143 | return 0; 1144 | } 1145 | gClientObj.sendMessagePort(buffer, len); 1146 | break; 1147 | } 1148 | } 1149 | break; 1150 | } 1151 | case IDC_ATTACH: 1152 | { 1153 | /* 1154 | * send: message = [FLAG | file name | NULL | file size | NULL | receiver | NULL | sender | NULL] 1155 | */ 1156 | for (auto chatbox : gPrivateChatBoxList) 1157 | { 1158 | if (chatbox->getHWND() == hwnd) 1159 | { 1160 | WCHAR message[1000]; 1161 | WCHAR buffer[1000]; 1162 | WCHAR* partner; 1163 | WCHAR* sender; 1164 | message[0] = MessageType::SEND_FILE; 1165 | BOOL isOpenFile = myCreateOpenFile(chatbox->getHWND(), buffer); 1166 | if (!isOpenFile) 1167 | { 1168 | break; 1169 | } 1170 | chatbox->onPressBtnAttach(buffer); 1171 | int lenBuf = wcslen(buffer); 1172 | int i; 1173 | for (i = lenBuf - 1; buffer[i] != L'\\' && i >= 0; i--) {} 1174 | 1175 | wcscpy(message + 1, &buffer[i + 1]); 1176 | DWORD size = chatbox->getSizeAndOpenFile(buffer); 1177 | 1178 | 1179 | int len = wcslen(message); 1180 | len++; 1181 | message[len++] = size >> 16; 1182 | message[len++] = size; 1183 | message[len++] = NULL; 1184 | 1185 | partner = message + len; 1186 | wcscpy(partner, chatbox->getPartner().c_str()); 1187 | len += chatbox->getPartner().size() + 1; 1188 | 1189 | sender = message + len; 1190 | wcscpy(sender, chatbox->getUsername().c_str()); 1191 | len += chatbox->getUsername().size(); 1192 | 1193 | gClientObj.sendMessagePort(message, len); 1194 | break; 1195 | } 1196 | } 1197 | break; 1198 | } 1199 | } 1200 | break; 1201 | } 1202 | return DefWindowProc(hwnd, message, wParam, lParam); 1203 | } 1204 | 1205 | 1206 | BOOL myCreateOpenFile(HWND hwnd, WCHAR* filename) 1207 | { 1208 | OPENFILENAMEW ofn = { 0 }; 1209 | WCHAR szFile[260]; 1210 | ofn.lStructSize = sizeof(ofn); 1211 | ofn.hwndOwner = hwnd; 1212 | ofn.lpstrFile = szFile; 1213 | ofn.lpstrFile[0] = '\0'; 1214 | ofn.nMaxFile = sizeof(szFile); 1215 | ofn.lpstrFilter = TEXT("All Files (*.*)\0*.*\0"); 1216 | ofn.nFilterIndex = 1; 1217 | ofn.lpstrFileTitle = NULL; 1218 | ofn.nMaxFileTitle = 0; 1219 | ofn.lpstrInitialDir = NULL; 1220 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; 1221 | if (!GetOpenFileName(&ofn)) 1222 | return FALSE; 1223 | wcscpy(filename, ofn.lpstrFile); 1224 | return TRUE; 1225 | } 1226 | 1227 | 1228 | BOOL myCreateSaveFile(HWND hwnd, WCHAR* path) 1229 | { 1230 | OPENFILENAMEW ofn = { 0 }; 1231 | WCHAR szFile[260]; 1232 | WCHAR ex[10]; 1233 | wcscpy(szFile, path); 1234 | int j; 1235 | for (j = wcslen(szFile) - 1; j > 0 && szFile[j] != L'.'; j--) {} 1236 | if (j != 0) 1237 | { 1238 | wcscpy(ex, szFile + j); 1239 | 1240 | } 1241 | ofn.lStructSize = sizeof(ofn); 1242 | ofn.hwndOwner = hwnd; 1243 | ofn.lpstrFile = szFile; 1244 | ofn.nMaxFile = sizeof(szFile); 1245 | ofn.lpstrFilter = TEXT("All Files (*.*)\0*.*\0"); 1246 | ofn.nFilterIndex = 1; 1247 | ofn.lpstrFileTitle = NULL; 1248 | ofn.nMaxFileTitle = 0; 1249 | ofn.lpstrInitialDir = NULL; 1250 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; 1251 | if (GetSaveFileName(&ofn) == FALSE) 1252 | return FALSE; 1253 | wcscpy(path, ofn.lpstrFile); 1254 | int i; 1255 | for (i = wcslen(path) - 1; i > 0 && path[i] != L'.'; i--) {} 1256 | 1257 | if (i == 0) 1258 | { 1259 | if (j != 0) 1260 | { 1261 | wcscat(path, ex); 1262 | } 1263 | } 1264 | return TRUE; 1265 | } 1266 | 1267 | LPWSTR convertSize(DWORD size) 1268 | { 1269 | WCHAR *buffer = new WCHAR[9]; 1270 | if (size / 1073741824 > 0) 1271 | { 1272 | wsprintf(buffer, L"%d.%d GB", size / 1073741824, (int)((double)(size % 1073741824) / (double)1073741824)) * 10; 1273 | } 1274 | else if (size / 1048576 > 0) 1275 | { 1276 | wsprintf(buffer, L"%d.%d MB", size / 1048576, (int)((float)(size % 1048576) / (float)1048576)) * 10; 1277 | } 1278 | else if (size / 1024 > 0) 1279 | { 1280 | wsprintf(buffer, L"%d.%d KB", size / 1024, (int)((float)(size % 1024) / (float)1024)) * 10; 1281 | } 1282 | else 1283 | { 1284 | wsprintf(buffer, L"%d Byte", size); 1285 | } 1286 | return buffer; 1287 | } -------------------------------------------------------------------------------- /Client/Client/Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ChatClient.h" 4 | #include "ThreadFunc.h" 5 | #include "resource.h" 6 | #include 7 | #include 8 | #include "PrivateChatBox.h" 9 | #include "GroupChatBox.h" 10 | #include 11 | #include 12 | #pragma comment(lib, "Winmm.lib") 13 | #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 14 | 15 | 16 | HWND hTextBox; 17 | HWND hMessageBox; 18 | HWND hSend; 19 | 20 | HWND hSignUp; 21 | HWND hLogIn; 22 | HWND hUsername; 23 | HWND hPassword; 24 | 25 | HWND hInvitedUsername; 26 | HWND hGroupName; 27 | HWND hPrivateChat; 28 | HWND hGroupChat; 29 | HWND hCreate; 30 | HWND hwnd; 31 | HFONT hFont; 32 | 33 | int gCurScene; 34 | list gPrivateChatBoxList; 35 | list gGroupChatBoxList; 36 | enum MessageType gCurMessageType; 37 | 38 | GdiplusStartupInput gdiplusStartupInput; 39 | ULONG_PTR gdiplusToken; 40 | 41 | LPWSTR convertSize(DWORD size); 42 | bool myRegClass(WNDPROC lpfnWndProc, WCHAR *szClassName, HINSTANCE hInst); 43 | BOOL myCreateOpenFile(HWND hwnd, WCHAR* filename); 44 | BOOL myCreateSaveFile(HWND hwnd, WCHAR* filename); 45 | 46 | LRESULT CALLBACK ChatBoxProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 47 | void OnDestroy(HWND hwnd); 48 | BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct); 49 | void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); 50 | void OnPaint(HWND hwnd); 51 | void OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem); -------------------------------------------------------------------------------- /Client/Client/Client.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Client/Client/Client.ico -------------------------------------------------------------------------------- /Client/Client/Client.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Client/Client/Client.rc -------------------------------------------------------------------------------- /Client/Client/Client.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {A55DB7D9-2882-4C8C-941F-E77602C2B648} 24 | Win32Proj 25 | Client 26 | 10.0.15063.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 91 | 92 | 93 | Windows 94 | 95 | 96 | 97 | 98 | Use 99 | Level3 100 | Disabled 101 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 102 | 103 | 104 | Windows 105 | 106 | 107 | 108 | 109 | Level3 110 | Use 111 | MaxSpeed 112 | true 113 | true 114 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 115 | 116 | 117 | Windows 118 | true 119 | true 120 | 121 | 122 | 123 | 124 | Level3 125 | Use 126 | MaxSpeed 127 | true 128 | true 129 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 130 | 131 | 132 | Windows 133 | true 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | Create 158 | Create 159 | Create 160 | Create 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /Client/Client/Client.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | 70 | 71 | Resource Files 72 | 73 | 74 | 75 | 76 | Resource Files 77 | 78 | 79 | 80 | 81 | Resource Files 82 | 83 | 84 | -------------------------------------------------------------------------------- /Client/Client/Definition.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | enum MessageType 3 | { 4 | PRIVATE_CHAT = 1, 5 | END_PRIVATE_CHAT, 6 | GROUP_CHAT, 7 | END_GROUP_CHAT, 8 | SIGNUP, 9 | SU_SUCCESS, 10 | SU_FAILURE, 11 | LOGIN, 12 | LI_SUCCESS, 13 | LI_FAILURE, 14 | CREATE_PRIVATE_CHAT, 15 | C_PC_SUCCESS, 16 | C_PC_FAILURE, 17 | CREATE_GROUP_CHAT, 18 | C_GC_SUCCESS, 19 | C_GC_FAILURE, 20 | GC_ADD_USER, 21 | GC_AU_SUCCESS, 22 | GC_AU_FAILURE, 23 | SEND_FILE, 24 | SF_ACCEPT, 25 | SF_CANCEL, 26 | FILE_DATA, 27 | CONTINUE, 28 | STOP, 29 | }; -------------------------------------------------------------------------------- /Client/Client/GroupChatBox.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "GroupChatBox.h" 3 | #include "resource.h" 4 | 5 | GroupChatBox * GroupChatBox::create(HWND hParent, HINSTANCE hInst, Point pos, Size size, wstring groupname) 6 | { 7 | auto cb = new (std::nothrow) GroupChatBox(); 8 | cb->_hInst = hInst; 9 | cb->_hParent = hParent; 10 | cb->_position = pos; 11 | cb->_size = size; 12 | cb->_groupname = groupname; 13 | 14 | groupname.erase(0, groupname.find_first_of(L';') + 1); 15 | cb->_hWnd = CreateWindowEx(0, L"chatbox", (groupname + L" - Group chat").c_str(), WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, pos.X, pos.Y, size.Width, size.Height, NULL, NULL, hInst, NULL); 16 | 17 | cb->_hAddUserBox = CreateWindow(L"edit", L"", WS_VISIBLE | WS_CHILD | WS_BORDER, size.Width*0.05, size.Height*0.03, size.Width*0.65, size.Height*0.05, cb->_hWnd, 0, hInst, 0); 18 | cb->_hAdd = CreateWindow(L"button", L"Add", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, size.Width*0.75, size.Height*0.03, size.Width*0.15, size.Height*0.05, cb->_hWnd, (HMENU)IDC_ADD, hInst, 0); 19 | 20 | cb->_hMessageBox = CreateWindowEx(WS_EX_RIGHTSCROLLBAR, L"edit", L"", WS_VISIBLE | WS_VSCROLL | WS_CHILD | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY | WS_BORDER, size.Width*0.05, size.Height*0.1, size.Width*0.9, size.Height*0.55, cb->_hWnd, 0, hInst, 0); 21 | cb->_hTextBox = CreateWindow(L"edit", L"", WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER, size.Width*0.05, size.Height*0.7, size.Width*0.65, size.Height*0.15, cb->_hWnd, 0, hInst, 0); 22 | cb->_hSend = CreateWindow(L"button", L"Send", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, size.Width*0.75, size.Height*0.7, size.Width*0.15, size.Height*0.15, cb->_hWnd, (HMENU)IDC_SEND_GROUP, hInst, 0); 23 | return cb; 24 | } 25 | 26 | void GroupChatBox::setFont(HFONT hFont) 27 | { 28 | SetWindowFont(_hMessageBox, hFont, true); 29 | SetWindowFont(_hTextBox, hFont, true); 30 | } 31 | 32 | wstring GroupChatBox::getGroupName() 33 | { 34 | return _groupname; 35 | } 36 | 37 | void GroupChatBox::receiveMessage(WCHAR * message) 38 | { 39 | WCHAR buffer[10000]; 40 | GetWindowText(_hMessageBox, buffer, 10000); 41 | wcscat(buffer, L"\r\n\r\n"); 42 | wcscat(buffer, message); 43 | SetWindowText(_hMessageBox, buffer); 44 | 45 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 46 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 47 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 48 | } 49 | 50 | int GroupChatBox::onPressBtnSend(WCHAR* message) 51 | { 52 | WCHAR bufferText[1000]; 53 | WCHAR bufferMess[10000]; 54 | message[0] = MessageType::GROUP_CHAT; 55 | message[1] = NULL; 56 | GetWindowText(_hTextBox, bufferText, 1000); 57 | SetWindowText(_hTextBox, L""); 58 | if (bufferText[0] == NULL) 59 | { 60 | return -1; 61 | } 62 | 63 | WCHAR* content; 64 | WCHAR* sender; 65 | int len; 66 | wcscat(message, _groupname.c_str()); 67 | 68 | len = _groupname.size() + 1; 69 | sender = message + ++len; 70 | wcscpy(sender, _username.c_str()); 71 | len += _username.size() + 1; 72 | 73 | content = message + len; 74 | wcscpy(content, bufferText); 75 | len += wcslen(bufferText); 76 | 77 | GetWindowText(_hMessageBox, bufferMess, 10000); 78 | wcscat(bufferMess, L"\r\n\r\n[You]:\r\n"); 79 | wcscat(bufferMess, bufferText); 80 | SetWindowText(_hMessageBox, bufferMess); 81 | 82 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 83 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 84 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 85 | 86 | return len; 87 | } 88 | 89 | int GroupChatBox::onPressBtnAdd(WCHAR * message) 90 | { 91 | WCHAR username[100]; 92 | message[0] = MessageType::GC_ADD_USER; 93 | message[1] = NULL; 94 | GetWindowText(_hAddUserBox, username, 50); 95 | SetWindowText(_hAddUserBox, L""); 96 | if (username == NULL) 97 | { 98 | return -1; 99 | } 100 | wcscat(message, _groupname.c_str()); 101 | int len = wcslen(message); 102 | len++; 103 | int i; 104 | for (i = 0; username[i] != NULL; i++) 105 | { 106 | message[len + i] = username[i]; 107 | } 108 | len += i; 109 | message[len] = NULL; 110 | return len; 111 | } 112 | 113 | void GroupChatBox::onUserLeft(WCHAR* user) 114 | { 115 | WCHAR buffer[10000]; 116 | GetWindowText(_hMessageBox, buffer, 10000); 117 | wcscat(buffer, L"\r\n\r\n\t\t"); 118 | wcscat(buffer, user); 119 | wcscat(buffer, L" has left this group chat.\r\n"); 120 | SetWindowText(_hMessageBox, buffer); 121 | 122 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 123 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 124 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 125 | } 126 | 127 | void GroupChatBox::onUserJoin(WCHAR* user) 128 | { 129 | WCHAR buffer[10000]; 130 | GetWindowText(_hMessageBox, buffer, 10000); 131 | wcscat(buffer, L"\r\n\r\n\t\t"); 132 | wcscat(buffer, user); 133 | wcscat(buffer, L" has joined this group chat.\r\n"); 134 | SetWindowText(_hMessageBox, buffer); 135 | 136 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 137 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 138 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 139 | } 140 | 141 | void GroupChatBox::setUsername(const wstring & username) 142 | { 143 | _username = username; 144 | } 145 | 146 | HWND GroupChatBox::getHWND() 147 | { 148 | return _hWnd; 149 | } 150 | 151 | GroupChatBox::GroupChatBox() 152 | { 153 | } 154 | 155 | 156 | GroupChatBox::~GroupChatBox() 157 | { 158 | } 159 | -------------------------------------------------------------------------------- /Client/Client/GroupChatBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Definition.h" 8 | #include 9 | 10 | #pragma comment(lib, "gdiplus.lib") 11 | using namespace Gdiplus; 12 | using namespace std; 13 | 14 | class GroupChatBox 15 | { 16 | private: 17 | HWND _hParent; 18 | HINSTANCE _hInst; 19 | HWND _hWnd; 20 | HWND _hAddUserBox; 21 | HWND _hAdd; 22 | HWND _hMessageBox; 23 | HWND _hTextBox; 24 | HWND _hSend; 25 | wstring _username; 26 | wstring _groupname; 27 | Point _position; 28 | Size _size; 29 | public: 30 | static GroupChatBox* create(HWND hParent, HINSTANCE hInst, Point pos, Size size, wstring groupname); 31 | void setFont(HFONT hFont); 32 | wstring getGroupName(); 33 | void receiveMessage(WCHAR* message); 34 | int onPressBtnSend(WCHAR* messageBuf); 35 | int onPressBtnAdd(WCHAR* addeduserBuf); 36 | void onUserLeft(WCHAR* user); 37 | void onUserJoin(WCHAR* user); 38 | void setUsername(const wstring& username); 39 | HWND getHWND(); 40 | GroupChatBox(); 41 | ~GroupChatBox(); 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /Client/Client/PrivateChatBox.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "PrivateChatBox.h" 3 | #include "resource.h" 4 | 5 | PrivateChatBox * PrivateChatBox::create(HWND hParent, HINSTANCE hInst, Point pos, Size size, wstring partner) 6 | { 7 | auto cb = new (std::nothrow) PrivateChatBox(); 8 | cb->_hWnd = CreateWindowEx(0, L"chatbox", (partner + L" - Private Chat").c_str(), WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, pos.X, pos.Y, size.Width, size.Height, NULL, NULL, hInst, NULL); 9 | cb->_hInst = hInst; 10 | cb->_hParent = hParent; 11 | cb->_position = pos; 12 | cb->_size = size; 13 | cb->_partnerUsername = partner; 14 | cb->_hMessageBox = CreateWindowEx(WS_EX_RIGHTSCROLLBAR, L"edit", L"", WS_VISIBLE | WS_VSCROLL | WS_CHILD | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY | WS_BORDER, size.Width*0.025, size.Height*0.025, size.Width*0.925, size.Height*0.635, cb->_hWnd, (HMENU)1000, hInst, 0); 15 | cb->_hTextBox = CreateWindow(L"edit", L"", WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER, size.Width*0.025, size.Height*0.7, size.Width*0.55, size.Height*0.15, cb->_hWnd, 0, hInst, 0); 16 | cb->_hSend = CreateWindow(L"button", L"Send", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, size.Width*0.6, size.Height*0.7, size.Width*0.15, size.Height*0.15, cb->_hWnd, (HMENU)IDC_SEND, hInst, 0); 17 | cb->_hAttach = CreateWindow(L"button", L"Attach", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, size.Width*0.775, size.Height*0.7, size.Width*0.15, size.Height*0.15, cb->_hWnd, (HMENU)IDC_ATTACH, hInst, 0); 18 | 19 | return cb; 20 | } 21 | 22 | 23 | wstring PrivateChatBox::getPartner() 24 | { 25 | return _partnerUsername; 26 | } 27 | 28 | void PrivateChatBox::setFont(HFONT hFont) 29 | { 30 | SetWindowFont(_hMessageBox, hFont, true); 31 | SetWindowFont(_hTextBox, hFont, true); 32 | } 33 | 34 | void PrivateChatBox::receiveMessage(WCHAR * message) 35 | { 36 | WCHAR buffer[10000]; 37 | GetWindowText(_hMessageBox, buffer, 10000); 38 | wcscat(buffer, L"\r\n\r\n$["); 39 | wcscat(buffer, _partnerUsername.c_str()); 40 | wcscat(buffer, L"]:\r\n"); 41 | wcscat(buffer, message); 42 | SetWindowText(_hMessageBox, buffer); 43 | 44 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 45 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 46 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 47 | } 48 | 49 | void PrivateChatBox::preReceiveFile(WCHAR * filename, DWORD filesize) 50 | { 51 | _filename = filename; 52 | _hReceiveFile = CreateFile(filename, // open file 53 | GENERIC_WRITE, // open for reading 54 | 0, // do not share 55 | NULL, // no security 56 | OPEN_ALWAYS, // always open 57 | FILE_ATTRIBUTE_NORMAL, // normal file 58 | NULL); // no attr. template 59 | 60 | if (_hReceiveFile == INVALID_HANDLE_VALUE) 61 | { 62 | return; // process error 63 | } 64 | _recFileSize = filesize; 65 | } 66 | 67 | BOOL PrivateChatBox::receiveFile(WCHAR * buffer, DWORD num) 68 | { 69 | DWORD dwBytesWrite; 70 | BOOL result = WriteFile(_hReceiveFile, buffer, num, &dwBytesWrite, NULL); 71 | if (!result) 72 | { 73 | MessageBox(_hWnd, L"Failed write", L"Error", 0); 74 | } 75 | _totalRecSize += dwBytesWrite; 76 | 77 | if (_totalRecSize >= _recFileSize) 78 | { 79 | CloseHandle(_hReceiveFile); 80 | _totalRecSize = 0; 81 | _recFileSize = 0; 82 | int result = MessageBox(_hWnd, L"Receive the file successfully.\n\rDo you want to open it?", L"Receive the file successfully", MB_YESNO); 83 | if (result == IDYES) 84 | { 85 | auto result2 = ShellExecute(_hWnd, L"open", _filename.c_str(), NULL, NULL, SW_SHOWNORMAL); 86 | if ((LONG)result2 == ERROR_FILE_NOT_FOUND || (LONG)result2 == ERROR_PATH_NOT_FOUND) 87 | { 88 | MessageBox(_hWnd, L"File not exist", 0, 0); 89 | } 90 | } 91 | _filename.clear(); 92 | return false; 93 | } 94 | return true; 95 | } 96 | 97 | int PrivateChatBox::onPressBtnSend(WCHAR* message) 98 | { 99 | WCHAR bufferText[1000]; 100 | WCHAR bufferMess[10000]; 101 | message[0] = MessageType::PRIVATE_CHAT; 102 | message[1] = NULL; 103 | GetWindowText(_hTextBox, bufferText, 1000); 104 | SetWindowText(_hTextBox, L""); 105 | if (bufferText[0] == NULL) 106 | { 107 | return -1; 108 | } 109 | 110 | WCHAR* content; 111 | WCHAR* sender; 112 | int len; 113 | wcscat(message, _partnerUsername.c_str()); 114 | 115 | len = _partnerUsername.size() + 1; 116 | sender = message + ++len; 117 | wcscpy(sender, _username.c_str()); 118 | len += _username.size() + 1; 119 | 120 | content = message + len; 121 | wcscpy(content, bufferText); 122 | len += wcslen(bufferText); 123 | 124 | GetWindowText(_hMessageBox, bufferMess, 10000); 125 | wcscat(bufferMess, L"\r\n\r\n[You]:\r\n"); 126 | wcscat(bufferMess, bufferText); 127 | SetWindowText(_hMessageBox, bufferMess); 128 | 129 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 130 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 131 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 132 | 133 | if (_isEndChat) 134 | { 135 | 136 | return -1; 137 | } 138 | 139 | return len; 140 | } 141 | 142 | void PrivateChatBox::onPressBtnAttach(WCHAR * filename) 143 | { 144 | if (_hSentFile != NULL) 145 | { 146 | MessageBox(_hWnd, L"You are sending a file. Wait it's done!", 0, 0); 147 | } 148 | } 149 | 150 | void PrivateChatBox::onRefuseReceiveFile() 151 | { 152 | CloseHandle(_hSentFile); 153 | _hSentFile = NULL; 154 | } 155 | 156 | void PrivateChatBox::onAcceptReceiveFile() 157 | { 158 | 159 | } 160 | 161 | DWORD PrivateChatBox::onSendFile(WCHAR * message) 162 | { 163 | 164 | /* 165 | * send: message = [FLAG | file size | NULL | receive | NULL | sender | NULL | content] 166 | */ 167 | int total = 0; 168 | DWORD dwBytesRead = 0; 169 | 170 | WCHAR buffer[600]; 171 | BOOL result = ReadFile(_hSentFile, buffer, 1024, &dwBytesRead, NULL); 172 | if (dwBytesRead == 0) 173 | { 174 | return 0; 175 | } 176 | message[0] = MessageType::FILE_DATA; 177 | int len = 1; 178 | message[len++] = dwBytesRead; 179 | message[len++] = NULL; 180 | WCHAR* partner; 181 | WCHAR* own; 182 | partner = message + len; 183 | wcscpy(partner, _partnerUsername.c_str()); 184 | len += _partnerUsername.size() + 2; 185 | own = message + len; 186 | wcscpy(own, _username.c_str()); 187 | len += _username.size() + 2; 188 | 189 | int i; 190 | if (dwBytesRead % 2 != 0) 191 | { 192 | dwBytesRead++; 193 | } 194 | int wsize = dwBytesRead / 2; 195 | 196 | for (i = 0; i < wsize; i++) 197 | { 198 | message[len + i] = buffer[i]; 199 | } 200 | len += wsize; 201 | return len; 202 | } 203 | 204 | void PrivateChatBox::onStop() 205 | { 206 | onRefuseReceiveFile(); 207 | } 208 | 209 | void PrivateChatBox::onEndChat() 210 | { 211 | WCHAR buffer[10000]; 212 | GetWindowText(_hMessageBox, buffer, 10000); 213 | wcscat(buffer, L"\r\n\r\n\tThis conversation was ended by your partner.\r\n"); 214 | SetWindowText(_hMessageBox, buffer); 215 | 216 | SendMessageA(_hMessageBox, EM_SETSEL, 0, -1); 217 | SendMessageA(_hMessageBox, EM_SETSEL, -1, -1); 218 | SendMessageA(_hMessageBox, EM_SCROLLCARET, 0, 0); 219 | _isEndChat = true; 220 | } 221 | 222 | void PrivateChatBox::setUsername(const wstring& username) 223 | { 224 | _username = username; 225 | } 226 | 227 | HWND PrivateChatBox::getHWND() 228 | { 229 | return _hWnd; 230 | } 231 | 232 | 233 | DWORD PrivateChatBox::getSizeAndOpenFile(WCHAR* filename) 234 | { 235 | _hSentFile = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 236 | 237 | if (_hSentFile == INVALID_HANDLE_VALUE) 238 | { 239 | return 0; 240 | } 241 | 242 | DWORD highSize; 243 | _sentFileSize = GetFileSize(_hSentFile, &highSize); 244 | //CloseHandle(hFile); 245 | return _sentFileSize; 246 | } 247 | 248 | DWORD PrivateChatBox::getSentFileSize() 249 | { 250 | return _sentFileSize; 251 | } 252 | 253 | DWORD PrivateChatBox::getRecFileSize() 254 | { 255 | return _recFileSize; 256 | } 257 | 258 | HANDLE PrivateChatBox::getHandleRecFile() 259 | { 260 | return _hReceiveFile; 261 | } 262 | 263 | HANDLE PrivateChatBox::getHandleSendFile() 264 | { 265 | return _hSentFile; 266 | } 267 | 268 | wstring PrivateChatBox::getUsername() 269 | { 270 | return _username; 271 | } 272 | 273 | PrivateChatBox::PrivateChatBox() 274 | { 275 | _isEndChat = false; 276 | _hSentFile = NULL; 277 | _hReceiveFile = NULL; 278 | _totalRecSize = 0; 279 | _recFileSize = 0; 280 | } 281 | 282 | 283 | PrivateChatBox::~PrivateChatBox() 284 | { 285 | } 286 | -------------------------------------------------------------------------------- /Client/Client/PrivateChatBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Definition.h" 8 | #include 9 | #include 10 | #pragma comment(lib, "gdiplus.lib") 11 | using namespace Gdiplus; 12 | using namespace std; 13 | 14 | class PrivateChatBox 15 | { 16 | private: 17 | HWND _hParent; 18 | HINSTANCE _hInst; 19 | HWND _hWnd; 20 | HWND _hMessageBox; 21 | HWND _hTextBox; 22 | HWND _hSend; 23 | HWND _hAttach; 24 | HANDLE _hSentFile; 25 | HANDLE _hReceiveFile; 26 | wstring _username; 27 | wstring _partnerUsername; 28 | wstring _filename; 29 | Point _position; 30 | Size _size; 31 | bool _isEndChat; 32 | DWORD _sentFileSize; 33 | DWORD _recFileSize; 34 | DWORD _totalRecSize; 35 | public: 36 | static PrivateChatBox* create(HWND hParent, HINSTANCE hInst, Point pos, Size size, wstring partner); 37 | wstring getPartner(); 38 | void setFont(HFONT hFont); 39 | void receiveMessage(WCHAR* message); 40 | int onPressBtnSend(WCHAR* messageBuf); 41 | void onPressBtnAttach(WCHAR* filename); 42 | void onRefuseReceiveFile(); 43 | void onAcceptReceiveFile(); 44 | DWORD onSendFile(WCHAR* message); 45 | void onStop(); 46 | void preReceiveFile(WCHAR* filename, DWORD filesize); 47 | BOOL receiveFile(WCHAR* buffer, DWORD num); 48 | void onEndChat(); 49 | void setUsername(const wstring& username); 50 | HWND getHWND(); 51 | DWORD getSizeAndOpenFile(WCHAR* filename); 52 | DWORD getSentFileSize(); 53 | DWORD getRecFileSize(); 54 | HANDLE getHandleRecFile(); 55 | HANDLE getHandleSendFile(); 56 | wstring getUsername(); 57 | PrivateChatBox(); 58 | ~PrivateChatBox(); 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /Client/Client/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | WIN32 APPLICATION : Client Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this Client application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your Client application. 9 | 10 | 11 | Client.vcxproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | Client.vcxproj.filters 18 | This is the filters file for VC++ projects generated using an Application Wizard. 19 | It contains information about the association between the files in your project 20 | and the filters. This association is used in the IDE to show grouping of files with 21 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 22 | "Source Files" filter). 23 | 24 | Client.cpp 25 | This is the main application source file. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | AppWizard has created the following resources: 29 | 30 | Client.rc 31 | This is a listing of all of the Microsoft Windows resources that the 32 | program uses. It includes the icons, bitmaps, and cursors that are stored 33 | in the RES subdirectory. This file can be directly edited in Microsoft 34 | Visual C++. 35 | 36 | Resource.h 37 | This is the standard header file, which defines new resource IDs. 38 | Microsoft Visual C++ reads and updates this file. 39 | 40 | Client.ico 41 | This is an icon file, which is used as the application's icon (32x32). 42 | This icon is included by the main resource file Client.rc. 43 | 44 | small.ico 45 | This is an icon file, which contains a smaller version (16x16) 46 | of the application's icon. This icon is included by the main resource 47 | file Client.rc. 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | Other standard files: 51 | 52 | StdAfx.h, StdAfx.cpp 53 | These files are used to build a precompiled header (PCH) file 54 | named Client.pch and a precompiled types file named StdAfx.obj. 55 | 56 | ///////////////////////////////////////////////////////////////////////////// 57 | Other notes: 58 | 59 | AppWizard uses "TODO:" comments to indicate parts of the source code you 60 | should add to or customize. 61 | 62 | ///////////////////////////////////////////////////////////////////////////// 63 | -------------------------------------------------------------------------------- /Client/Client/ThreadFunc.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ChatClient.h" 3 | #include "ThreadFunc.h" 4 | 5 | ChatClient gClientObj; 6 | 7 | UINT recMessageThread(LPVOID lParam) 8 | { 9 | while (1) 10 | { 11 | if (gClientObj.recMessagePort()) 12 | break; 13 | } 14 | return 0; 15 | } -------------------------------------------------------------------------------- /Client/Client/ThreadFunc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | extern ChatClient gClientObj; 5 | 6 | UINT recMessageThread(LPVOID lParam); -------------------------------------------------------------------------------- /Client/Client/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Client.rc 4 | // 5 | #define IDC_MYICON 2 6 | #define ID_CHATBOX 9 7 | #define IDD_CLIENT_DIALOG 102 8 | #define IDS_APP_TITLE 103 9 | #define IDD_ABOUTBOX 103 10 | #define IDM_ABOUT 104 11 | #define IDM_EXIT 105 12 | #define IDI_CLIENT 107 13 | #define IDC_CLIENT 109 14 | #define IDC_LOGIN 110 15 | #define IDC_SIGNUP 111 16 | #define IDC_SEND 112 17 | #define IDC_RECEIVE 113 18 | #define IDC_PRIVATECHAT 114 19 | #define IDC_GROUPCHAT 115 20 | #define IDC_CREATE 116 21 | #define IDC_ADD 117 22 | #define IDC_SEND_GROUP 118 23 | #define IDC_ATTACH 119 24 | #define IDR_MAINFRAME 128 25 | #define IDI_SMALL 131 26 | #define IDR_WAVE1 132 27 | #define IDC_STATIC -1 28 | 29 | // Next default values for new objects 30 | // 31 | #ifdef APSTUDIO_INVOKED 32 | #ifndef APSTUDIO_READONLY_SYMBOLS 33 | #define _APS_NO_MFC 1 34 | #define _APS_NEXT_RESOURCE_VALUE 133 35 | #define _APS_NEXT_COMMAND_VALUE 32771 36 | #define _APS_NEXT_CONTROL_VALUE 1003 37 | #define _APS_NEXT_SYMED_VALUE 120 38 | #endif 39 | #endif 40 | -------------------------------------------------------------------------------- /Client/Client/server.ini: -------------------------------------------------------------------------------- 1 | # Input server's ip address 2 | 192.168.1.1 -------------------------------------------------------------------------------- /Client/Client/sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Client/Client/sound.wav -------------------------------------------------------------------------------- /Client/Client/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // Client.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /Client/Client/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | 12 | // C RunTime Header Files 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | // TODO: reference additional headers your program requires here 20 | -------------------------------------------------------------------------------- /Client/Client/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /Demo/client1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client1.jpg -------------------------------------------------------------------------------- /Demo/client2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client2.jpg -------------------------------------------------------------------------------- /Demo/client3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client3.jpg -------------------------------------------------------------------------------- /Demo/client4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client4.jpg -------------------------------------------------------------------------------- /Demo/client5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client5.jpg -------------------------------------------------------------------------------- /Demo/client6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/client6.jpg -------------------------------------------------------------------------------- /Demo/server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Demo/server.jpg -------------------------------------------------------------------------------- /Docs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Docs.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClientServerChat-CppSocket 2 | 3 | The Win32 C++ application about client-server chat with socket. 4 | The project was created in Computer network course, FIT, HCMUS, VN. 5 | 6 | ## Demo 7 | 8 | Youtube: https://www.youtube.com/watch?v=qeX0n49aelI 9 | 10 | ### Server side: 11 | 12 | 13 | ### Client side: 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ## More info 23 | - Win32 C++ (WinAPI C++) 24 | - Socket in C 25 | - Client-server chat 26 | 27 | #### Author: Le Hoang Vu 28 | -------------------------------------------------------------------------------- /Release/Client/Client.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Release/Client/Client.exe -------------------------------------------------------------------------------- /Release/Client/server.ini: -------------------------------------------------------------------------------- 1 | # Input server's ip address 2 | 127.0.0.1 -------------------------------------------------------------------------------- /Release/Server/Server.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Release/Server/Server.exe -------------------------------------------------------------------------------- /Release/Server/userdata.ini: -------------------------------------------------------------------------------- 1 | flash-E1;123 2 | flash-E2;123 3 | -------------------------------------------------------------------------------- /Server/Server.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{A1777B93-2CB9-4E70-9ECA-618395FB64CB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Debug|x64.ActiveCfg = Debug|x64 17 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Debug|x64.Build.0 = Debug|x64 18 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Debug|x86.Build.0 = Debug|Win32 20 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Release|x64.ActiveCfg = Release|x64 21 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Release|x64.Build.0 = Release|x64 22 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Release|x86.ActiveCfg = Release|Win32 23 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Server/Server/ChatServer.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ChatServer.h" 3 | #include "ThreadFunc.h" 4 | #include "Resource.h" 5 | ChatServer::ChatServer() 6 | { 7 | cout << "Starting up TCP Chat server\n"; 8 | _isConnected = false; 9 | 10 | WSADATA wsaData; 11 | 12 | sockaddr_in local; 13 | 14 | int wsaret = WSAStartup(0x101, &wsaData); 15 | 16 | if (wsaret != 0) 17 | { 18 | return; 19 | } 20 | 21 | local.sin_family = AF_INET; 22 | local.sin_addr.s_addr = INADDR_ANY; 23 | local.sin_port = htons((u_short)8084); 24 | 25 | _socListenClient = socket(AF_INET, SOCK_STREAM, 0); 26 | 27 | 28 | if (_socListenClient == INVALID_SOCKET) 29 | { 30 | return; 31 | } 32 | 33 | 34 | if (bind(_socListenClient, (sockaddr*)&local, sizeof(local)) != 0) 35 | { 36 | return; 37 | } 38 | 39 | 40 | if (listen(_socListenClient, 10) != 0) 41 | { 42 | return; 43 | } 44 | 45 | _isConnected = true; 46 | return; 47 | } 48 | 49 | ChatServer::~ChatServer() 50 | { 51 | closesocket(_socListenClient); 52 | 53 | WSACleanup(); 54 | 55 | if (_clientList.size() != 0) 56 | { 57 | for (auto client: _clientList) 58 | { 59 | delete client; 60 | } 61 | } 62 | 63 | if (_groupchatList.size() != 0) 64 | { 65 | for (auto group : _groupchatList) 66 | { 67 | delete group; 68 | } 69 | } 70 | } 71 | 72 | bool ChatServer::isConnected() 73 | { 74 | return _isConnected; 75 | } 76 | 77 | void ChatServer::startListenClient() 78 | { 79 | 80 | sockaddr_in from; 81 | int fromlen = sizeof(from); 82 | 83 | _socClient = accept(_socListenClient, 84 | (struct sockaddr*)&from, &fromlen); 85 | auto packet = new ClientPacket; 86 | packet->socket = _socClient; 87 | 88 | if (_socClient != INVALID_SOCKET) 89 | { 90 | SendMessage(_hwnd, WM_COMMAND, ID_USER_CONNECT, 0); 91 | _clientList.push_back(packet); 92 | 93 | } 94 | 95 | AfxBeginThread(recServerThread, (void *)_socClient); 96 | 97 | } 98 | 99 | 100 | 101 | int ChatServer::sendMessageClient(ClientPacket* client, WCHAR* message, int len) 102 | { 103 | int iStat = 0; 104 | 105 | iStat = send(client->socket, (char*)message, len * 2 + 2, 0); 106 | if (iStat == -1) 107 | _clientList.remove(client); 108 | if (iStat == -1) 109 | return 1; 110 | 111 | return 0; 112 | 113 | } 114 | 115 | int ChatServer::recClient(SOCKET recSocket) 116 | { 117 | WCHAR* message; 118 | WCHAR temp[4096]; 119 | int iStat; 120 | int len; 121 | iStat = recv(recSocket, (char*)temp, 4096, 0); 122 | list::iterator itl; 123 | for (itl = _clientList.begin(); itl != _clientList.end(); itl++) 124 | { 125 | if ((*itl)->socket == recSocket) 126 | { 127 | break; 128 | } 129 | } 130 | 131 | if (iStat == -1) 132 | { 133 | for (auto gc : _groupchatList) 134 | { 135 | gc->username.remove((*itl)->username); 136 | } 137 | _clientList.remove((*itl)); 138 | SendMessage(_hwnd, WM_COMMAND, ID_USER_LEAVE, 0); 139 | return 1; 140 | } 141 | else 142 | { 143 | message = temp; 144 | switch (message[0]) 145 | { 146 | case MessageType::PRIVATE_CHAT: 147 | { 148 | /* 149 | * receive: message = [FLAG | receiver | NULL | sender | NULL | content | NULL] 150 | * receive: message = [FLAG | receiver | NULL | sender | NULL | content | NULL] 151 | */ 152 | 153 | WCHAR* partner; 154 | partner = message + 1; 155 | 156 | 157 | for (auto client : _clientList) 158 | { 159 | if (wcscmp(client->username.c_str(), partner) == 0) 160 | { 161 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 162 | break; 163 | } 164 | } 165 | break; 166 | } 167 | case MessageType::GROUP_CHAT: 168 | { 169 | /* 170 | * receive: message = [FLAG | group name | NULL | sender | NULL | content | NULL] 171 | * send: message = [FLAG | group name | NULL | sender | NULL | content | NULL] 172 | */ 173 | WCHAR* groupname; 174 | len = wcslen(message); 175 | groupname = message + 1; 176 | 177 | sendMessageGroup(groupname, (*itl)->username, message, iStat / 2); 178 | break; 179 | } 180 | case MessageType::END_PRIVATE_CHAT: 181 | { 182 | /* 183 | * receive: message = [FLAG | receiver | NULL | sender | NULL] 184 | * send: message = [FLAG | receiver | NULL | sender | NULL] 185 | */ 186 | WCHAR* receiver = message + 1; 187 | 188 | for (auto client : _clientList) 189 | { 190 | if (wcscmp(client->username.c_str(), receiver) == 0) 191 | { 192 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 193 | break; 194 | } 195 | } 196 | break; 197 | } 198 | case MessageType::END_GROUP_CHAT: 199 | { 200 | /* 201 | * receive: message = [FLAG | group name | NULL | sender | NULL] 202 | * send: message = [FLAG | group name | NULL | sender | NULL] 203 | */ 204 | 205 | WCHAR* groupname; 206 | GroupChat* gc = NULL; 207 | 208 | groupname = message + 1; 209 | for (auto group : _groupchatList) 210 | { 211 | // Tìm thấy group chat 212 | if (wcscmp(group->name.c_str(), groupname) == 0) 213 | { 214 | for (auto user : group->username) 215 | { 216 | // Những user khác user gửi tin nhắn sẽ được chọn 217 | // Gửi tin nhắn đến hết các user được chọn 218 | if (user != (*itl)->username) 219 | { 220 | // Tìm ClientPacket của user 221 | for (auto client : _clientList) 222 | { 223 | // Tìm thấy ClientPacket 224 | if (client->username == user) 225 | { 226 | // Gửi tin nhắn đến user 227 | sendMessageClient(client, message, iStat / 2); 228 | break; 229 | } 230 | } 231 | } 232 | } 233 | gc = group; 234 | break; 235 | } 236 | } 237 | if (!gc) 238 | { 239 | break; 240 | } 241 | if (gc->username.size() > 0) 242 | { 243 | gc->username.remove((*itl)->username); 244 | } 245 | 246 | if (gc->username.size() == 0) 247 | { 248 | if (_groupchatList.size() > 0) 249 | { 250 | _groupchatList.remove(gc); 251 | } 252 | } 253 | break; 254 | } 255 | case MessageType::SIGNUP: 256 | { 257 | /* 258 | * receive: message = [FLAG | user name | NULL | password | NULL] 259 | * send: message = [FLAG] 260 | */ 261 | 262 | WCHAR* username; 263 | WCHAR* password; 264 | 265 | username = message + 1; 266 | password = message + wcslen(message) + 1; 267 | 268 | auto user = new User; 269 | user->username = username; 270 | user->password = password; 271 | int result = signUp(user); 272 | if (result == true) 273 | { 274 | message[0] = MessageType::SU_SUCCESS; 275 | SendMessage(_hwnd, WM_COMMAND, ID_RESTORE, 0); 276 | } 277 | else 278 | { 279 | message[0] = MessageType::SU_FAILURE; 280 | } 281 | sendMessageClient((*itl), (WCHAR*)message, 1); 282 | break; 283 | } 284 | case MessageType::LOGIN: 285 | { 286 | /* 287 | * receive: message = [FLAG | user name | NULL | password | NULL] 288 | * send: message = [FLAG] 289 | */ 290 | 291 | WCHAR* username; 292 | WCHAR* password; 293 | username = message + 1; 294 | password = message + wcslen(message) + 1; 295 | User user; 296 | user.username = username; 297 | user.password = password; 298 | int result = logIn(recSocket, user); 299 | if (result == true) 300 | { 301 | message[0] = MessageType::LI_SUCCESS; 302 | } 303 | else 304 | { 305 | message[0] = MessageType::LI_FAILURE; 306 | } 307 | 308 | sendMessageClient((*itl), (WCHAR*)message, 1); 309 | break; 310 | } 311 | case MessageType::CREATE_PRIVATE_CHAT: 312 | { 313 | /* 314 | * receive: message = [FLAG | partner | NULL] 315 | * send: message = [FLAG | parter | NULL] - Succeed 316 | * [FLAG] - Fail 317 | */ 318 | 319 | WCHAR* partner; 320 | partner = message + 1; 321 | bool result = isOnlineUser(partner); 322 | if (result == true) 323 | { 324 | 325 | message[0] = MessageType::C_PC_SUCCESS; 326 | sendMessageClient((*itl), (WCHAR*)message, iStat / 2); 327 | } 328 | else 329 | { 330 | message[0] = MessageType::C_PC_FAILURE; 331 | sendMessageClient((*itl), (WCHAR*)message, 1); 332 | } 333 | break; 334 | } 335 | case MessageType::CREATE_GROUP_CHAT: 336 | { 337 | /* 338 | * receive: message = [FLAG | group name | NULL] 339 | * send: message = [FLAG | group name | NULL] - Succeed 340 | * [FLAG] - Fail 341 | */ 342 | 343 | WCHAR buffer[150]; 344 | buffer[0] = C_GC_SUCCESS; 345 | buffer[1] = NULL; 346 | wcscat(buffer, (*itl)->username.c_str()); 347 | wcscat(buffer, L";"); 348 | wcscat(buffer, message + 1); 349 | bool result = isOnlineGroup(buffer + 1); 350 | if (result == false) 351 | { 352 | auto gc = new (std::nothrow) GroupChat; 353 | gc->name = buffer + 1; 354 | gc->username.push_back((*itl)->username); 355 | _groupchatList.push_back(gc); 356 | len = wcslen(buffer); 357 | sendMessageClient((*itl), (WCHAR*)buffer, len); 358 | } 359 | else 360 | { 361 | buffer[0] = MessageType::C_GC_FAILURE; 362 | sendMessageClient((*itl), (WCHAR*)buffer, 1); 363 | } 364 | break; 365 | } 366 | case MessageType::GC_ADD_USER: 367 | { 368 | /* 369 | * receive: message = [FLAG | group name | NULL | added user | NULL] 370 | * send: message = [FLAG | group name | NULL | added user | NULL] 371 | */ 372 | WCHAR* username; 373 | WCHAR* groupname; 374 | len = wcslen(message); 375 | 376 | groupname = message + 1; 377 | username = message + len + 1; 378 | 379 | bool result = isOnlineUser(username); 380 | if (result == true) 381 | { 382 | for (auto group : _groupchatList) 383 | { 384 | if (wcscmp(group->name.c_str(), groupname) == 0) 385 | { 386 | for (auto user : group->username) 387 | { 388 | if (wcscmp(user.c_str(), username) == 0) 389 | { 390 | message[0] = MessageType::GC_AU_FAILURE; 391 | len += group->username.size() + 1; 392 | sendMessageClient((*itl), (WCHAR*)message, len); 393 | return 0; 394 | } 395 | } 396 | group->username.push_back(username); 397 | 398 | message[0] = MessageType::GC_AU_SUCCESS; 399 | 400 | len += wcslen(username) + 1; 401 | 402 | sendMessageGroup(groupname, L"user", message, len, true); 403 | //SendMessageClient((*itl), (WCHAR*)buffer, len); 404 | return 0; 405 | } 406 | } 407 | } 408 | else 409 | { 410 | message[0] = MessageType::GC_AU_FAILURE; 411 | sendMessageClient((*itl), (WCHAR*)message, len); 412 | } 413 | break; 414 | } 415 | case MessageType::SF_ACCEPT: 416 | case MessageType::SF_CANCEL: 417 | { 418 | /* 419 | * receive: message = [FLAG | file name | NULL | file size | NULL | sender | NULL| receiver | NULL] 420 | * send: message = [FLAG | file name | NULL | file size | NULL | sender | NULL| receiver | NULL] 421 | */ 422 | WCHAR* partner; 423 | partner = message + wcslen(message) + 4; 424 | partner += wcslen(partner) + 1; 425 | //buffer[len] = NULL; 426 | for (auto client : _clientList) 427 | { 428 | if (wcscmp(client->username.c_str(), partner) == 0) 429 | { 430 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 431 | break; 432 | } 433 | } 434 | break; 435 | } 436 | case MessageType::SEND_FILE: 437 | { 438 | /* 439 | * receive: message = [FLAG | file name | NULL | file size | NULL | receiver | NULL| sender | NULL] 440 | * send: message = [FLAG | file name | NULL | file size | NULL | receiver | NULL| sender | NULL] 441 | */ 442 | WCHAR* partner; 443 | partner = message + wcslen(message) + 4; 444 | 445 | //buffer[len] = NULL; 446 | for (auto client : _clientList) 447 | { 448 | if (wcscmp(client->username.c_str(), partner) == 0) 449 | { 450 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 451 | break; 452 | } 453 | } 454 | break; 455 | } 456 | case MessageType::FILE_DATA: 457 | { 458 | /* 459 | * receive: message = [FLAG | file size | NULL | receiver | NULL | sender | NULL | content] 460 | * send: message = [FLAG | file size | NULL | receiver | NULL | sender | NULL | content] 461 | */ 462 | 463 | WCHAR* receiver; 464 | receiver = message + 3; 465 | 466 | //buffer[len] = NULL; 467 | for (auto client : _clientList) 468 | { 469 | if (wcscmp(client->username.c_str(), receiver) == 0) 470 | { 471 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 472 | break; 473 | } 474 | } 475 | break; 476 | } 477 | case MessageType::STOP: 478 | case MessageType::CONTINUE: 479 | { 480 | /* 481 | * receive: message = [FLAG | receiver | NULL | sender | NULL] 482 | * send: message = [FLAG | receiver | NULL | sender | NULL] 483 | */ 484 | WCHAR* receiver = message + 1; 485 | for (auto client : _clientList) 486 | { 487 | if (wcscmp(client->username.c_str(), receiver) == 0) 488 | { 489 | sendMessageClient(client, (WCHAR*)message, iStat / 2); 490 | break; 491 | } 492 | } 493 | 494 | break; 495 | } 496 | } 497 | 498 | return 0; 499 | } 500 | return 0; 501 | 502 | } 503 | 504 | void ChatServer::setHWND(HWND hwnd) 505 | { 506 | _hwnd = hwnd; 507 | } 508 | 509 | bool ChatServer::signUp(User* user) 510 | { 511 | for (auto userdata : _userData) 512 | { 513 | if (user->username == userdata->username) 514 | { 515 | return false; 516 | } 517 | } 518 | _userData.push_back(user); 519 | return true; 520 | } 521 | 522 | bool ChatServer::logIn(SOCKET socket, User user) 523 | { 524 | for (auto userdata : _userData) 525 | { 526 | if (user.username == userdata->username && user.password == userdata->password) 527 | { 528 | for (auto client : _clientList) 529 | { 530 | if (client->socket == socket) 531 | { 532 | client->username = user.username; 533 | return true; 534 | } 535 | } 536 | } 537 | } 538 | return false; 539 | } 540 | 541 | bool ChatServer::isOnlineUser(wstring username) 542 | { 543 | for (auto client : _clientList) 544 | { 545 | if (username == client->username) 546 | { 547 | return true; 548 | } 549 | } 550 | return false; 551 | } 552 | 553 | bool ChatServer::isOnlineGroup(wstring groupname) 554 | { 555 | for (auto group : _groupchatList) 556 | { 557 | if (group->name == groupname) 558 | { 559 | return true; 560 | } 561 | } 562 | return false; 563 | } 564 | 565 | void ChatServer::sendMessageGroup(wstring groupname, wstring sender, WCHAR* message, int len, bool isSendToSender) 566 | { 567 | // Tìm group chat trong danh sách 568 | for (auto group : _groupchatList) 569 | { 570 | // Tìm thấy group chat 571 | if (group->name == groupname) 572 | { 573 | // Tim user trong danh sách của group tìm thấy 574 | for (auto user : group->username) 575 | { 576 | // Những user khác user gửi tin nhắn sẽ được chọn 577 | // Gửi tin nhắn đến hết các user được chọn 578 | if (user != sender || isSendToSender) 579 | { 580 | // Tìm ClientPacket của user 581 | for (auto client : _clientList) 582 | { 583 | // Tìm thấy ClientPacket 584 | if (client->username == user) 585 | { 586 | // Gửi tin nhắn đến user 587 | sendMessageClient(client, (WCHAR*)message, len); 588 | break; 589 | } 590 | } 591 | 592 | } 593 | } 594 | // Đã gửi tin nhắn hết các user - thoát! 595 | return; 596 | } 597 | } 598 | } 599 | 600 | void ChatServer::addUser(User* user) 601 | { 602 | _userData.push_back(user); 603 | } 604 | 605 | list& ChatServer::getUser() 606 | { 607 | return _userData; 608 | } 609 | -------------------------------------------------------------------------------- /Server/Server/ChatServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _AFXDLL 3 | #include "Definition.h" 4 | 5 | class ChatServer 6 | { 7 | public: 8 | ChatServer(); 9 | ~ChatServer(); 10 | bool isConnected(); 11 | void startListenClient(); 12 | int sendMessageClient(ClientPacket* client, WCHAR* message, int len); 13 | int recClient(SOCKET recSocket); 14 | void setHWND(HWND hwnd); 15 | bool signUp(User* user); 16 | bool logIn(SOCKET socket, User user); 17 | bool isOnlineUser(wstring username); 18 | bool isOnlineGroup(wstring groupname); 19 | void sendMessageGroup(wstring groupname, wstring sender, WCHAR* message, int len, bool isSendToSender = false); 20 | void addUser(User* user); 21 | list& getUser(); 22 | private: 23 | bool _isConnected; 24 | int _serverPort; 25 | list _clientList; 26 | SOCKET _socClient; 27 | SOCKET _socListenClient; 28 | HWND _hwnd; 29 | list _userData; 30 | list _groupchatList; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /Server/Server/Definition.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | struct ClientPacket 12 | { 13 | SOCKET socket; 14 | wstring username; 15 | }; 16 | 17 | struct User 18 | { 19 | wstring username; 20 | wstring password; 21 | }; 22 | 23 | struct GroupChat 24 | { 25 | wstring name; 26 | list username; 27 | }; 28 | 29 | enum MessageType 30 | { 31 | PRIVATE_CHAT = 1, 32 | END_PRIVATE_CHAT, 33 | GROUP_CHAT, 34 | END_GROUP_CHAT, 35 | SIGNUP, 36 | SU_SUCCESS, 37 | SU_FAILURE, 38 | LOGIN, 39 | LI_SUCCESS, 40 | LI_FAILURE, 41 | CREATE_PRIVATE_CHAT, 42 | C_PC_SUCCESS, 43 | C_PC_FAILURE, 44 | CREATE_GROUP_CHAT, 45 | C_GC_SUCCESS, 46 | C_GC_FAILURE, 47 | GC_ADD_USER, 48 | GC_AU_SUCCESS, 49 | GC_AU_FAILURE, 50 | SEND_FILE, 51 | SF_ACCEPT, 52 | SF_CANCEL, 53 | FILE_DATA, 54 | CONTINUE, 55 | STOP, 56 | }; 57 | -------------------------------------------------------------------------------- /Server/Server/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | WIN32 APPLICATION : Server Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this Server application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your Server application. 9 | 10 | 11 | Server.vcxproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | Server.vcxproj.filters 18 | This is the filters file for VC++ projects generated using an Application Wizard. 19 | It contains information about the association between the files in your project 20 | and the filters. This association is used in the IDE to show grouping of files with 21 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 22 | "Source Files" filter). 23 | 24 | Server.cpp 25 | This is the main application source file. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | AppWizard has created the following resources: 29 | 30 | Server.rc 31 | This is a listing of all of the Microsoft Windows resources that the 32 | program uses. It includes the icons, bitmaps, and cursors that are stored 33 | in the RES subdirectory. This file can be directly edited in Microsoft 34 | Visual C++. 35 | 36 | Resource.h 37 | This is the standard header file, which defines new resource IDs. 38 | Microsoft Visual C++ reads and updates this file. 39 | 40 | Server.ico 41 | This is an icon file, which is used as the application's icon (32x32). 42 | This icon is included by the main resource file Server.rc. 43 | 44 | small.ico 45 | This is an icon file, which contains a smaller version (16x16) 46 | of the application's icon. This icon is included by the main resource 47 | file Server.rc. 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | Other standard files: 51 | 52 | StdAfx.h, StdAfx.cpp 53 | These files are used to build a precompiled header (PCH) file 54 | named Server.pch and a precompiled types file named StdAfx.obj. 55 | 56 | ///////////////////////////////////////////////////////////////////////////// 57 | Other notes: 58 | 59 | AppWizard uses "TODO:" comments to indicate parts of the source code you 60 | should add to or customize. 61 | 62 | ///////////////////////////////////////////////////////////////////////////// 63 | -------------------------------------------------------------------------------- /Server/Server/Server.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Server/Server/Server.aps -------------------------------------------------------------------------------- /Server/Server/Server.cpp: -------------------------------------------------------------------------------- 1 | // Server.cpp : Defines the entry point for the application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "Server.h" 6 | 7 | #define MAX_LOADSTRING 100 8 | 9 | // Global Variables: 10 | HINSTANCE hInst; // current instance 11 | WCHAR szTitle[MAX_LOADSTRING]; // The title bar text 12 | WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name 13 | 14 | // Forward declarations of functions included in this code module: 15 | ATOM MyRegisterClass(HINSTANCE hInstance); 16 | BOOL InitInstance(HINSTANCE, int); 17 | LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 18 | INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 19 | 20 | int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 21 | _In_opt_ HINSTANCE hPrevInstance, 22 | _In_ LPWSTR lpCmdLine, 23 | _In_ int nCmdShow) 24 | { 25 | UNREFERENCED_PARAMETER(hPrevInstance); 26 | UNREFERENCED_PARAMETER(lpCmdLine); 27 | 28 | // TODO: Place code here. 29 | 30 | // Initialize global strings 31 | LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 32 | LoadStringW(hInstance, IDC_SERVER, szWindowClass, MAX_LOADSTRING); 33 | MyRegisterClass(hInstance); 34 | 35 | // Perform application initialization: 36 | if (!InitInstance (hInstance, nCmdShow)) 37 | { 38 | return FALSE; 39 | } 40 | 41 | HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SERVER)); 42 | 43 | MSG msg; 44 | 45 | // Main message loop: 46 | while (GetMessage(&msg, nullptr, 0, 0)) 47 | { 48 | if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 49 | { 50 | TranslateMessage(&msg); 51 | DispatchMessage(&msg); 52 | } 53 | } 54 | 55 | return (int) msg.wParam; 56 | } 57 | 58 | 59 | 60 | // 61 | // FUNCTION: MyRegisterClass() 62 | // 63 | // PURPOSE: Registers the window class. 64 | // 65 | ATOM MyRegisterClass(HINSTANCE hInstance) 66 | { 67 | WNDCLASSEXW wcex; 68 | 69 | wcex.cbSize = sizeof(WNDCLASSEX); 70 | 71 | wcex.style = CS_HREDRAW | CS_VREDRAW; 72 | wcex.lpfnWndProc = WndProc; 73 | wcex.cbClsExtra = 0; 74 | wcex.cbWndExtra = 0; 75 | wcex.hInstance = hInstance; 76 | wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SERVER)); 77 | wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 78 | wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 79 | wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SERVER); 80 | wcex.lpszClassName = szWindowClass; 81 | wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 82 | 83 | return RegisterClassExW(&wcex); 84 | } 85 | 86 | // 87 | // FUNCTION: InitInstance(HINSTANCE, int) 88 | // 89 | // PURPOSE: Saves instance handle and creates main window 90 | // 91 | // COMMENTS: 92 | // 93 | // In this function, we save the instance handle in a global variable and 94 | // create and display the main program window. 95 | // 96 | BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 97 | { 98 | hInst = hInstance; // Store instance handle in our global variable 99 | 100 | HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 101 | CW_USEDEFAULT, 0, 500, 500, nullptr, nullptr, hInstance, nullptr); 102 | 103 | if (!hWnd) 104 | { 105 | return FALSE; 106 | } 107 | 108 | ShowWindow(hWnd, nCmdShow); 109 | UpdateWindow(hWnd); 110 | 111 | return TRUE; 112 | } 113 | 114 | // 115 | // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 116 | // 117 | // PURPOSE: Processes messages for the main window. 118 | // 119 | // WM_COMMAND - process the application menu 120 | // WM_PAINT - Paint the main window 121 | // WM_DESTROY - post a quit message and return 122 | // 123 | // 124 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 125 | { 126 | switch (message) 127 | { 128 | case WM_CREATE: 129 | { 130 | char buf[4096]; 131 | if (!gServerObj.isConnected()) 132 | { 133 | MessageBox(0, L"\nFailed to initialise server socket.", 0, 0); 134 | getch(); 135 | return 1; 136 | } 137 | gServerObj.setHWND(hWnd); 138 | AfxBeginThread(listenServerThread, 0); 139 | LoadData(); 140 | hTextBox = CreateWindow(L"edit", L"", WS_VISIBLE | WS_VSCROLL | WS_CHILD | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY, 0, 0, 480, 500, hWnd, 0, hInst, 0); 141 | break; 142 | } 143 | case WM_COMMAND: 144 | { 145 | int wmId = LOWORD(wParam); 146 | // Parse the menu selections: 147 | switch (wmId) 148 | { 149 | case IDM_ABOUT: 150 | DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 151 | break; 152 | case IDM_EXIT: 153 | DestroyWindow(hWnd); 154 | break; 155 | case ID_RESTORE: 156 | RestoreData(); 157 | break; 158 | case ID_USER_CONNECT: 159 | { 160 | WCHAR buffer[10000]; 161 | GetWindowText(hTextBox, buffer, 10000); 162 | if (buffer == NULL) 163 | { 164 | break; 165 | } 166 | wcscat(buffer, L"\r\n"); 167 | wcscat(buffer, L"1 user has just connected to server"); 168 | SetWindowText(hTextBox, buffer); 169 | 170 | SendMessageA(hTextBox, EM_SETSEL, 0, -1); 171 | SendMessageA(hTextBox, EM_SETSEL, -1, -1); 172 | SendMessageA(hTextBox, EM_SCROLLCARET, 0, 0); 173 | 174 | break; 175 | } 176 | case ID_USER_LEAVE: 177 | { 178 | WCHAR buffer[10000]; 179 | GetWindowText(hTextBox, buffer, 10000); 180 | if (buffer == NULL) 181 | { 182 | break; 183 | } 184 | wcscat(buffer, L"\r\n"); 185 | wcscat(buffer, L"1 user has just disconnected to server"); 186 | SetWindowText(hTextBox, buffer); 187 | 188 | SendMessageA(hTextBox, EM_SETSEL, 0, -1); 189 | SendMessageA(hTextBox, EM_SETSEL, -1, -1); 190 | SendMessageA(hTextBox, EM_SCROLLCARET, 0, 0); 191 | 192 | break; 193 | } 194 | default: 195 | return DefWindowProc(hWnd, message, wParam, lParam); 196 | } 197 | } 198 | break; 199 | case WM_PAINT: 200 | { 201 | PAINTSTRUCT ps; 202 | HDC hdc = BeginPaint(hWnd, &ps); 203 | // TODO: Add any drawing code that uses hdc here... 204 | EndPaint(hWnd, &ps); 205 | } 206 | break; 207 | case WM_DESTROY: 208 | RestoreData(); 209 | PostQuitMessage(0); 210 | break; 211 | default: 212 | return DefWindowProc(hWnd, message, wParam, lParam); 213 | } 214 | return 0; 215 | } 216 | 217 | // Message handler for about box. 218 | INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 219 | { 220 | UNREFERENCED_PARAMETER(lParam); 221 | switch (message) 222 | { 223 | case WM_INITDIALOG: 224 | return (INT_PTR)TRUE; 225 | 226 | case WM_COMMAND: 227 | if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 228 | { 229 | EndDialog(hDlg, LOWORD(wParam)); 230 | return (INT_PTR)TRUE; 231 | } 232 | break; 233 | } 234 | return (INT_PTR)FALSE; 235 | } 236 | 237 | 238 | void RestoreData() 239 | { 240 | std::wofstream fos(L"userdata.ini"); 241 | std::locale loc(std::locale(), new std::codecvt_utf8); 242 | fos.imbue(loc); 243 | if (!fos.is_open()) 244 | return; 245 | for (auto user : gServerObj.getUser()) 246 | { 247 | fos << user->username << L";"; 248 | fos << user->password << L"\n"; 249 | } 250 | fos.close(); 251 | } 252 | 253 | void LoadData() 254 | { 255 | std::locale loc(std::locale(), new std::codecvt_utf8); 256 | std::wifstream fin(L"userdata.ini"); 257 | fin.imbue(loc); 258 | WCHAR buffer[200]; 259 | WCHAR username[100]; 260 | WCHAR password[100]; 261 | 262 | int i = 0; 263 | 264 | while (!fin.eof()) 265 | { 266 | fin.getline(buffer, 200); 267 | if (buffer[0] == NULL) 268 | { 269 | break; 270 | } 271 | int len = wcslen(buffer); 272 | int j, k; 273 | for (j = 0; j < len; j++) 274 | { 275 | if (buffer[j] == L';') 276 | { 277 | j++; 278 | break; 279 | } 280 | username[j] = buffer[j]; 281 | } 282 | username[j - 1] = NULL; 283 | 284 | for (k = j; k < len; k++) 285 | { 286 | password[k - j] = buffer[k]; 287 | } 288 | password[k - j] = NULL; 289 | auto user = new User; 290 | user->username = username; 291 | user->password = password; 292 | gServerObj.addUser(user); 293 | } 294 | 295 | fin.close(); 296 | } -------------------------------------------------------------------------------- /Server/Server/Server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ChatServer.h" 4 | #include "ThreadFunc.h" 5 | #include "resource.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | HWND hTextBox; 14 | 15 | void LoadData(); 16 | void RestoreData(); -------------------------------------------------------------------------------- /Server/Server/Server.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Server/Server/Server.ico -------------------------------------------------------------------------------- /Server/Server/Server.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elhoangvu/ClientServerChat-C-Socket/af135387688d46728d0b707a718ab8de93720488/Server/Server/Server.rc -------------------------------------------------------------------------------- /Server/Server/Server.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {A1777B93-2CB9-4E70-9ECA-618395FB64CB} 24 | Win32Proj 25 | Server 26 | 10.0.15063.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 91 | 92 | 93 | Windows 94 | 95 | 96 | 97 | 98 | Use 99 | Level3 100 | Disabled 101 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 102 | 103 | 104 | Windows 105 | 106 | 107 | 108 | 109 | Level3 110 | Use 111 | MaxSpeed 112 | true 113 | true 114 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 115 | 116 | 117 | Windows 118 | true 119 | true 120 | 121 | 122 | 123 | 124 | Level3 125 | Use 126 | MaxSpeed 127 | true 128 | true 129 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 130 | 131 | 132 | Windows 133 | true 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | Create 155 | Create 156 | Create 157 | Create 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /Server/Server/Server.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | 58 | 59 | Resource Files 60 | 61 | 62 | 63 | 64 | Resource Files 65 | 66 | 67 | -------------------------------------------------------------------------------- /Server/Server/ThreadFunc.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ChatServer.h" 3 | #include "ThreadFunc.h" 4 | ChatServer gServerObj; 5 | 6 | UINT recServerThread(LPVOID lParam) 7 | { 8 | SOCKET recSocket = (SOCKET)lParam; 9 | while (1) 10 | { 11 | if (gServerObj.recClient(recSocket)) 12 | break; 13 | } 14 | return 0; 15 | } 16 | 17 | UINT listenServerThread(LPVOID lParam) 18 | { 19 | while (1) 20 | gServerObj.startListenClient(); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /Server/Server/ThreadFunc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | extern ChatServer gServerObj; 4 | 5 | UINT listenServerThread(LPVOID lParam); 6 | UINT recServerThread(LPVOID lParam); -------------------------------------------------------------------------------- /Server/Server/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Server.rc 4 | // 5 | #define IDC_MYICON 2 6 | #define IDD_SERVER_DIALOG 102 7 | #define IDS_APP_TITLE 103 8 | #define IDD_ABOUTBOX 103 9 | #define IDM_ABOUT 104 10 | #define IDM_EXIT 105 11 | #define IDI_SERVER 107 12 | #define IDI_SMALL 108 13 | #define IDC_SERVER 109 14 | #define IDC_RECEIVE 110 15 | #define ID_RESTORE 111 16 | #define ID_USER_CONNECT 112 17 | #define ID_USER_LEAVE 113 18 | #define IDR_MAINFRAME 128 19 | #define IDC_STATIC -1 20 | 21 | // Next default values for new objects 22 | // 23 | #ifdef APSTUDIO_INVOKED 24 | #ifndef APSTUDIO_READONLY_SYMBOLS 25 | #define _APS_NO_MFC 1 26 | #define _APS_NEXT_RESOURCE_VALUE 129 27 | #define _APS_NEXT_COMMAND_VALUE 32771 28 | #define _APS_NEXT_CONTROL_VALUE 1000 29 | #define _APS_NEXT_SYMED_VALUE 114 30 | #endif 31 | #endif 32 | -------------------------------------------------------------------------------- /Server/Server/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // Server.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /Server/Server/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | 13 | 14 | // C RunTime Header Files 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | // TODO: reference additional headers your program requires here 22 | -------------------------------------------------------------------------------- /Server/Server/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | --------------------------------------------------------------------------------