關於socket的文章,園子裡面有很多,其實無非就是 WSAStartup、socket、bind、listen、accept、recv、send(服務端),WSAStartup、socket、connect、send、recv(客戶端)的使用。今天第一次看socket,也只學會了socket阻塞模式,即一個服務端對一個客戶端,別的客戶端想連接也連接不上--個人理解,不知道對不對。
為了解決一(服務端)對多(客戶端)的問題,自作聰明在服務端用上了多線程。初步效果還不錯:
我暈,1080P的筆記本上截的圖,放上來這麼大!
上代碼,
TCPServer.cpp
1 /**************************************************************************** 2 /* 文 件 名:server.cpp * 3 /* 作 者:ZhangXuliang * 4 /* 日 期:2015/05/24 星期日 11:28 * 5 /* 版 本 號:V 1.0.0 * 6 /* 版權說明:Copyright(c)2015 ZXL. All rights reserved. * 7 /* 描 述:多線程處理Client連接 * 8 /****************************************************************************/ 9 10 #define _CRT_SECURE_NO_WARNINGS 11 12 #include <iostream> 13 #include <windows.h> 14 #include <process.h> 15 16 //#include <WinSock2.h> 17 #pragma comment(lib,"Ws2_32.lib") 18 19 #define SERVER_IP "127.0.0.1" 20 #define SERVER_PORT 9999 21 #define BUFSIZE (1024) 22 23 //定義一個結構體,存儲客戶端的sClient、sockaddr_in結構 24 typedef struct _ClientStruct 25 { 26 SOCKET csSocket; 27 SOCKADDR_IN csSockAddr_In; 28 } ClientStruct, *LPClientStruct; 29 30 //處理後續客戶端事件的程序 31 void ClientEven(PVOID param); 32 33 using namespace std; 34 int main() 35 { 36 int ErrCode; //錯誤代碼 37 int addrlen; 38 WSADATA WSAdata; 39 SOCKET sServer, sClient; 40 SOCKADDR_IN saServer, saClient; 41 42 //初始化 43 ErrCode = WSAStartup(MAKEWORD(2, 2), &WSAdata); 44 if (ErrCode) 45 { 46 cout << "WSAStartup()出錯!錯誤代碼:#" << ErrCode << endl; 47 WSACleanup(); 48 return -1; 49 } 50 51 //創建套接字 52 sServer = socket(AF_INET, SOCK_STREAM,0); 53 if (INVALID_SOCKET == sServer) 54 { 55 cout << "socket()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 56 WSACleanup(); 57 return -2; 58 } 59 60 //初始化saServer 61 saServer.sin_family = AF_INET; 62 saServer.sin_addr.S_un.S_addr = htons(INADDR_ANY); 63 saServer.sin_port = htons(SERVER_PORT); 64 //綁定監聽IP和端口,以便下面的listen()監聽 65 ErrCode = bind(sServer, (SOCKADDR *)&saServer, sizeof(SOCKADDR_IN)); 66 if (SOCKET_ERROR == ErrCode) 67 { 68 cout << "bind()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 69 WSACleanup(); 70 return -3; 71 } 72 73 //開始監聽 74 ErrCode = listen(sServer, 10); 75 if (SOCKET_ERROR == ErrCode) 76 { 77 cout << "listen()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 78 WSACleanup(); 79 return -4; 80 } 81 cout << "正在監聽端口:" << SERVER_PORT << endl; 82 83 while (TRUE) 84 { 85 //接受來自客戶端的連接請求 86 addrlen = sizeof(saClient); 87 sClient = accept(sServer, (SOCKADDR *)&saClient, &addrlen); 88 if (INVALID_SOCKET == sClient) 89 { 90 cout << "accpet()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 91 } 92 93 //啟動一個線程,來處理後續的客戶端事件 94 LPClientStruct lpcsClient = new ClientStruct; //個人覺得這兒用 new 是一個創舉啊,因為在啟動線程後,如果直接將ClientStruct指針 95 lpcsClient->csSocket = sClient; //傳到ClientEven()中,那麼,當有中一個Client接入時,會改變ClientEven中的ClientStruct內容 96 lpcsClient->csSockAddr_In = saClient; //所以這兒用new,再在線程中delete 97 _beginthread(ClientEven,0, lpcsClient); 98 99 } 100 WSACleanup(); 101 return 0; 102 } 103 104 105 void ClientEven(PVOID param) 106 { 107 int ErrCode; 108 109 char buf[BUFSIZE] = { 0 }; 110 SYSTEMTIME time; 111 GetLocalTime(&time); 112 sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); 113 114 //設置下輸出的字體的顏色 115 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 116 SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN); 117 cout << buf << "\t" << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" 118 << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t接入服務器" << endl; 119 //還原字體顏色 120 SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); 121 //連接成功,先發送一條成功的消息 122 ErrCode = send(((LPClientStruct)param)->csSocket, "連接服務器成功", strlen("連接到服務器成功!"),0); 123 if (SOCKET_ERROR == ErrCode) 124 { 125 cout << "send()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 126 delete (LPClientStruct)param; 127 _endthread(); 128 } 129 130 while (TRUE) 131 { 132 //接受消息 133 ZeroMemory(buf, BUFSIZE); 134 ErrCode = recv(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 135 if (SOCKET_ERROR == ErrCode) 136 { 137 if (WSAGetLastError() == 10054) 138 { 139 SetConsoleTextAttribute(hConsole, FOREGROUND_RED); 140 cout << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t被強制斷開連接!" << endl; 141 SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); 142 } 143 else 144 { 145 cout << "recv()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 146 } 147 148 delete (LPClientStruct)param; //退出線程之前先delete前面new的ClientStruct 149 _endthread(); 150 } 151 152 cout << inet_ntoa(((LPClientStruct)param)->csSockAddr_In.sin_addr) << ":" << ((LPClientStruct)param)->csSockAddr_In.sin_port << "\t說:" << buf << endl; 153 154 //反饋消息 155 //if ((pPipe = _popen(buf, "rt")) != NULL) 156 //{ 157 // while (!feof(pPipe)) 158 // { 159 // fgets(szResult, 32, pPipe); 160 // strcat(buf, szResult); 161 // if (strlen(buf) == 1024) 162 // { 163 // buf[1024] = 'c'; //c --continue 164 // send(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 165 // ZeroMemory(buf, BUFSIZE); 166 // } 167 // } 168 // _pclose(pPipe); 169 //} 170 //else 171 // strcpy(buf, "打開匿名管道失敗!"); 172 173 ErrCode = send(((LPClientStruct)param)->csSocket, buf, sizeof(buf), 0); 174 175 if (SOCKET_ERROR == ErrCode) 176 { 177 cout << "send()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 178 179 delete (LPClientStruct)param; //退出線程之前先delete前面new的ClientStruct 180 _endthread(); 181 } 182 } 183 delete (LPClientStruct)param; //退出線程之前先delete前面new的ClientStruct 184 _endthread(); 185 }
下面是TCPClient.cpp
1 /**************************************************************************** 2 /* 文 件 名:Client.cpp * 3 /* 作 者:ZhangXuliang * 4 /* 日 期:2015/05/23 星期六 23:30 * 5 /* 版 本 號:V 1.0.0 * 6 /* 版權說明:Copyright(c)2015 ZXL. All rights reserved. * 7 /* 描 述:Client客戶端 * 8 /****************************************************************************/ 9 10 #define _CRT_SECURE_NO_WARNINGS 11 12 #include <iostream> 13 #include <string> 14 15 #include <winsock.h> 16 #pragma comment(lib,"ws2_32.lib") 17 18 using namespace std; 19 20 #define BUFSIZE 1024 21 #define PORT 9999 22 //#define SERVER_IP "127.0.0.1" 23 24 int main() 25 { 26 WSAData wsaData; 27 SOCKET sHost; 28 sockaddr_in addrServ; 29 char buf[BUFSIZE]; 30 int retVal; 31 32 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 33 { 34 cout << "WSAStartup()失敗!" << endl; 35 return -1; 36 } 37 38 sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 39 if (INVALID_SOCKET == sHost) 40 { 41 cout << "socket()出錯!" << endl; 42 WSACleanup(); 43 return -1; 44 } 45 46 addrServ.sin_family = AF_INET; 47 addrServ.sin_port = htons(PORT); 48 addrServ.sin_addr.S_un.S_addr = inet_addr("192.168.1.223"); 49 50 retVal = connect(sHost, (LPSOCKADDR)&addrServ, sizeof(addrServ)); 51 if (SOCKET_ERROR == retVal) 52 { 53 cout << "connect()出錯!" << endl; 54 closesocket(sHost); 55 WSACleanup(); 56 return -1; 57 } 58 retVal = recv(sHost, buf, sizeof(buf) + 1, 0); 59 cout << "從服務器接受:" << buf << endl << endl << endl; 60 while (TRUE) 61 { 62 cout << "輸入要發給服務器的內容:"; 63 //string msg; 64 //getline(cin.msg); 65 char msg[BUFSIZE]; 66 //cin.getline(msg, BUFSIZE); 67 cin >> msg; 68 //cout << endl; 69 ZeroMemory(buf, BUFSIZE); 70 strcpy(buf, msg); 71 72 retVal = send(sHost, buf, strlen(buf), 0); 73 if (SOCKET_ERROR == retVal) 74 { 75 cout << "發送失敗!" << endl; 76 closesocket(sHost); 77 WSACleanup(); 78 return -1; 79 } 80 81 retVal = recv(sHost, buf, sizeof(buf) + 1, 0); 82 if (SOCKET_ERROR == retVal) 83 { 84 cout << "recv()出錯!錯誤代碼:#" << WSAGetLastError() << endl; 85 closesocket(sHost); 86 WSACleanup(); 87 return -1; 88 } 89 cout << "從服務器接受:" << buf << endl << endl; 90 91 92 if (strcmp(buf, "quit") == 0) 93 { 94 cout << "quit" << endl; 95 break; 96 } 97 } 98 99 closesocket(sHost); 100 WSACleanup(); 101 102 return 0; 103 104 105 106 }