單個服務器對多個客戶端程序:ios
一。簡要說明編程
二。查看效果小程序
三。編寫思路服務器
四。程序源代碼網絡
五。存在問題多線程
一。簡要說明:app
程序名爲:TcpSocketOneServerToMulClientsocket
程序功能:實現單個服務器對多個客戶端通信功能的小程序。
PS: 這是繼上次簡單的 Tcp Windows Socket 編程後的再一程序,程序實現依然不是很嚴謹,還待完善~函數
二。查看效果:ui
三。編寫思路:
由上一次的程序思路來看,若是想實現單個服務器對多個客戶端程序的通信的話,此次程序編寫嘗試從多線程的角度來考慮問題:
在服務器的實現中:能夠main函數做爲主線程,不斷地接客戶端的鏈接請求。
再新建子線程——每鏈接一個客戶端,就專爲這個客戶端新建一個用於實現接收信息並顯示到屏幕上功能的子線程。
而後,新建子線程,專用於本機發送消息。
在客戶端的實現中:主線程負責鏈接服務器,新建子線程,用於從服務器接收信息,再建子線程,用於從客戶端向服務器中發送信息。
總的來講,也能夠理解爲,單個服務器的進程利用這個服務器中的線程與多個客戶端進程進行通信。
四。程序源代碼:
1 // OneServerMain.cpp 2 3 #include <iostream> 4 #include <cstdio> 5 #include <string> 6 #include <cstring> 7 #include <vector> 8 #include <iterator> 9 #include <algorithm> 10 #include <Winsock2.h> 11 #include <Windows.h> 12 13 using namespace std; 14 HANDLE bufferMutex; // 令其能互斥成功正常通訊的信號量句柄 15 SOCKET sockConn; // 客戶端的套接字 16 vector <SOCKET> clientSocketGroup; 17 18 int main() 19 { 20 // 加載socket動態連接庫(dll) 21 WORD wVersionRequested; 22 WSADATA wsaData; // 這結構是用於接收Wjndows Socket的結構信息的 23 wVersionRequested = MAKEWORD( 2, 2 ); // 請求2.2版本的WinSock庫 24 int err = WSAStartup( wVersionRequested, &wsaData ); 25 if ( err != 0 ) { 26 return -1; // 返回值爲零的時候是表示成功申請WSAStartup 27 } 28 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { // 檢測是否2.2版本的socket庫 29 WSACleanup( ); 30 return -1; 31 } 32 33 // 建立socket操做,創建流式套接字,返回套接字號sockSrv 34 SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0); 35 36 // 套接字sockSrv與本地地址相連 37 SOCKADDR_IN addrSrv; 38 addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 將INADDR_ANY轉換爲網絡字節序,調用 htonl(long型)或htons(整型) 39 addrSrv.sin_family = AF_INET; 40 addrSrv.sin_port = htons(6000); 41 42 if(SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))){ // 第二參數要強制類型轉換 43 return -1; 44 } 45 46 // 將套接字設置爲監聽模式(鏈接請求), listen()通知TCP服務器準備好接收鏈接 47 listen(sockSrv, 20); 48 49 cout << "服務器已成功就緒,若服務器想發送信息給客戶端,可直接輸入內容後按回車.\n"; 50 // accept(),接收鏈接,等待客戶端鏈接 51 52 bufferMutex = CreateSemaphore(NULL, 1, 1, NULL); 53 54 DWORD WINAPI SendMessageThread(LPVOID IpParameter); 55 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); 56 57 HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL); 58 59 while(true){ // 不斷等待客戶端請求的到來 60 sockConn = accept(sockSrv, NULL, NULL); 61 if (SOCKET_ERROR != sockConn){ 62 clientSocketGroup.push_back(sockConn); 63 } 64 HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, (LPVOID)sockConn, 0, NULL); 65 WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被佔用) 66 if(NULL == receiveThread) { 67 printf("\nCreatThread AnswerThread() failed.\n"); 68 } 69 else{ 70 printf("\nCreate Receive Client Thread OK.\n"); 71 } 72 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 73 } 74 75 WaitForSingleObject(sendThread, INFINITE); // 等待線程結束 76 CloseHandle(sendThread); 77 CloseHandle(bufferMutex); 78 WSACleanup(); // 終止對套接字庫的使用 79 printf("\n"); 80 system("pause"); 81 return 0; 82 } 83 84 85 DWORD WINAPI SendMessageThread(LPVOID IpParameter) 86 { 87 while(1){ 88 string talk; 89 getline(cin, talk); 90 WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被佔用) 91 /* if("quit" == talk){ 92 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 93 return 0; 94 } 95 else*/ 96 { 97 talk.append("\n"); 98 } 99 printf("I Say:(\"quit\"to exit):"); 100 cout << talk; 101 for(int i = 0; i < clientSocketGroup.size(); ++i){ 102 // send(clientSocketGroup[i], talk.c_str(), talk.size(), 0); // 發送信息 103 send(clientSocketGroup[i], talk.c_str(), 200, 0); // 發送信息 104 } 105 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 106 } 107 return 0; 108 } 109 110 111 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter) 112 { 113 SOCKET ClientSocket=(SOCKET)(LPVOID)IpParameter; 114 while(1){ 115 char recvBuf[300]; 116 recv(ClientSocket, recvBuf, 200, 0); 117 WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被佔用) 118 119 if (recvBuf[0] == 'q' && recvBuf[1] == 'u' && recvBuf[2] == 'i' && recvBuf[3] == 't' && recvBuf[4] == '\0'){ 120 vector<SOCKET>::iterator result = find(clientSocketGroup.begin(), clientSocketGroup.end(), ClientSocket); 121 clientSocketGroup.erase(result); 122 closesocket(ClientSocket); 123 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 124 printf("\nAttention: A Client has leave...\n", 200, 0); 125 break; 126 } 127 128 printf("%s Says: %s\n", "One Client", recvBuf); // 接收信息 129 130 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 131 } 132 return 0; 133 }
1 // MulClientMain.cpp 2 3 #include <iostream> 4 #include <cstdio> 5 #include <string> 6 #include <cstring> 7 #include <winsock2.h> 8 #include <Windows.h> 9 10 using namespace std; 11 12 SOCKET sockClient; // 鏈接成功後的套接字 13 HANDLE bufferMutex; // 令其能互斥成功正常通訊的信號量句柄 14 15 int main() 16 { 17 // 加載socket動態連接庫(dll) 18 WORD wVersionRequested; 19 WSADATA wsaData; // 這結構是用於接收Wjndows Socket的結構信息的 20 wVersionRequested = MAKEWORD( 2, 2 ); // 請求2.2版本的WinSock庫 21 int err = WSAStartup( wVersionRequested, &wsaData ); 22 if ( err != 0 ) { // 返回值爲零的時候是表示成功申請WSAStartup 23 return -1; 24 } 25 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { // 檢查版本號是否正確 26 WSACleanup( ); 27 return -1; 28 } 29 30 // 建立socket操做,創建流式套接字,返回套接字號sockClient 31 sockClient = socket(AF_INET, SOCK_STREAM, 0); 32 if(sockClient == INVALID_SOCKET) { 33 printf("Error at socket():%ld\n", WSAGetLastError()); 34 WSACleanup(); 35 return -1; 36 } 37 38 // 將套接字sockClient與遠程主機相連 39 // int connect( SOCKET s, const struct sockaddr* name, int namelen); 40 // 第一個參數:須要進行鏈接操做的套接字 41 // 第二個參數:設定所須要鏈接的地址信息 42 // 第三個參數:地址的長度 43 SOCKADDR_IN addrSrv; 44 addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本地迴路地址是127.0.0.1; 45 addrSrv.sin_family = AF_INET; 46 addrSrv.sin_port = htons(6000); 47 connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); 48 cout << "本客戶端已準備就緒,用戶可直接輸入文字向服務器反饋信息。\n"; 49 50 // send(sockClient, "\nAttention: A Client has enter...\n", strlen("Attention: A Client has enter...\n")+1, 0); 51 send(sockClient, "\nAttention: A Client has enter...\n", 200, 0); 52 53 bufferMutex = CreateSemaphore(NULL, 1, 1, NULL); 54 55 DWORD WINAPI SendMessageThread(LPVOID IpParameter); 56 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); 57 58 HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL); 59 HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, NULL, 0, NULL); 60 61 62 WaitForSingleObject(sendThread, INFINITE); // 等待線程結束 63 closesocket(sockClient); 64 CloseHandle(sendThread); 65 CloseHandle(receiveThread); 66 CloseHandle(bufferMutex); 67 WSACleanup(); // 終止對套接字庫的使用 68 69 printf("End linking...\n"); 70 printf("\n"); 71 system("pause"); 72 return 0; 73 } 74 75 76 DWORD WINAPI SendMessageThread(LPVOID IpParameter) 77 { 78 while(1){ 79 string talk; 80 getline(cin, talk); 81 WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被佔用) 82 if("quit" == talk){ 83 talk.push_back('\0'); 84 // send(sockClient, talk.c_str(), talk.size(), 0); 85 send(sockClient, talk.c_str(), 200, 0); 86 break; 87 } 88 else{ 89 talk.append("\n"); 90 } 91 printf("\nI Say:(\"quit\"to exit):"); 92 cout << talk; 93 // send(sockClient, talk.c_str(), talk.size(), 0); // 發送信息 94 send(sockClient, talk.c_str(), 200, 0); // 發送信息 95 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 96 } 97 return 0; 98 } 99 100 101 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter) 102 { 103 while(1){ 104 char recvBuf[300]; 105 recv(sockClient, recvBuf, 200, 0); 106 WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被佔用) 107 108 printf("%s Says: %s\n", "Server", recvBuf); // 接收信息 109 110 ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源佔用完畢) 111 } 112 return 0; 113 }
五。存在問題:
1. 將客戶端異常退出(不按程序要求輸入「quit」退出),而直接用ALT+F4關閉後,服務器會出現死循環顯示亂碼的狀況。
2. 在未啓動服務器前提下,直接啓動客戶端時出現死循環亂碼狀況。
3. 服務器輸入「quit」時不能正常退出。
轉http://blog.csdn.net/neicole/article/details/7539444