[socket编程] 一个服务器与多个客户端之间通信
Posted NULL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[socket编程] 一个服务器与多个客户端之间通信相关的知识,希望对你有一定的参考价值。
转自:http://blog.csdn.net/neicole/article/details/7539444 并加以改进
Server程序:
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 DWORD WINAPI SendMessageThread(LPVOID IpParameter); 19 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); 20 21 int main() 22 { 23 // 加载socket动态链接库(dll) 24 WORD wVersionRequested; 25 WSADATA wsaData; // 该结构用于接收Wjndows Socket的结构信息 26 wVersionRequested = MAKEWORD( 2, 2 ); // 请求2.2版本的WinSock库 27 int err = WSAStartup( wVersionRequested, &wsaData ); 28 if ( err != 0 ) 29 { 30 return -1; // 返回值为零的时候是表示成功申请WSAStartup 31 } 32 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) // 检测是否2.2版本的socket库 33 { 34 WSACleanup( ); 35 return -1; 36 } 37 38 // 创建socket操作,建立流式套接字,返回套接字号sockSrv 39 SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0); 40 41 // 套接字sockSrv与本地地址相连 42 SOCKADDR_IN addrSrv; 43 addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 将INADDR_ANY转换为网络字节序,调用 htonl(long型)或htons(整型) 44 addrSrv.sin_family = AF_INET; 45 addrSrv.sin_port = htons(6000); 46 47 // 绑定套接字 48 if(SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))) 49 { 50 return -1; 51 } 52 53 // 将套接字设置为监听模式(连接请求),listen()通知TCP服务器准备好接收连接,最大连接数为20 54 listen(sockSrv, 20); 55 56 cout << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车.\n"; 57 58 // 设置信号量的值 59 bufferMutex = CreateSemaphore(NULL, 1, 1, NULL); 60 61 // 建立一个向所有客户端发送信息的线程 62 HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL); 63 64 while(true)// 不断等待客户端请求的到来 65 { 66 sockConn = accept(sockSrv, NULL, NULL); 67 if (SOCKET_ERROR != sockConn) 68 { 69 clientSocketGroup.push_back(sockConn); //若连接成功则将客户端套接字加入clientSocketGroup 70 } 71 72 // 建立一个接收指定客户端信息的线程 73 HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, (LPVOID)sockConn, 0, NULL); 74 75 WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用则使用资源) 76 if(NULL == receiveThread) 77 { 78 cout << "CreatThread AnswerThread() failed." << endl; 79 } 80 else 81 { 82 cout << "Create Receive Client Thread OK." << endl; 83 } 84 ReleaseSemaphore(bufferMutex, 1, NULL); // V操作(资源占用完毕将其释放) 85 } 86 87 WaitForSingleObject(sendThread, INFINITE); // 等待线程结束 88 CloseHandle(sendThread); 89 CloseHandle(bufferMutex); 90 WSACleanup(); // 终止对套接字库的使用 91 system("pause"); 92 return 0; 93 } 94 95 96 DWORD WINAPI SendMessageThread(LPVOID IpParameter) 97 { 98 while(1) 99 { 100 string talk; 101 getline(cin, talk); 102 WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用) 103 104 //若服务器要主动关闭 105 if("quit" == talk) 106 { 107 talk.push_back(‘\0‘); 108 for(int i = 0; i < clientSocketGroup.size(); ++i) 109 { 110 send(clientSocketGroup[i], talk.c_str(), talk.size(), 0); // 发送信息 111 } 112 ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕) 113 exit(0); 114 } 115 116 cout << "I Say:(\"quit\"to exit):" << talk << endl; 117 118 for(int i = 0; i < clientSocketGroup.size(); ++i) 119 { 120 send(clientSocketGroup[i], talk.c_str(), talk.size(), 0); // 发送信息 121 } 122 ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕) 123 } 124 return 0; 125 } 126 127 128 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter) 129 { 130 SOCKET ClientSocket=(SOCKET)(LPVOID)IpParameter; 131 while(1) 132 { 133 char recvBuf[300]; 134 int recv_id = recv(ClientSocket, recvBuf, 200, 0); 135 136 WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用) 137 138 if(recv_id == -1) 139 { 140 vector<SOCKET>::iterator result = find(clientSocketGroup.begin(), clientSocketGroup.end(), ClientSocket); 141 clientSocketGroup.erase(result); 142 closesocket(ClientSocket); 143 ReleaseSemaphore(bufferMutex, 1, NULL); // V操作(资源占用完毕) 144 cout << "Attention: A Client accidentally dropped ..." << endl; 145 break; 146 } 147 148 //若从客户端收到了"quit"命令则将该客户端套接字从clientSocketGroup中清除并关闭 149 if (recvBuf[0] == ‘q‘ && recvBuf[1] == ‘u‘ && recvBuf[2] == ‘i‘ && recvBuf[3] == ‘t‘ && recvBuf[4] == ‘\0‘) 150 { 151 vector<SOCKET>::iterator result = find(clientSocketGroup.begin(), clientSocketGroup.end(), ClientSocket); 152 clientSocketGroup.erase(result); 153 closesocket(ClientSocket); 154 ReleaseSemaphore(bufferMutex, 1, NULL); // V操作(资源占用完毕) 155 cout << "Attention: A Client has left ..." << endl; 156 break; 157 } 158 159 cout << "One Client Says: " << recvBuf << endl; // 接收信息 160 161 ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕) 162 } 163 return 0; 164 }
Client程序:
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 DWORD WINAPI SendMessageThread(LPVOID IpParameter); 16 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); 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 ) // 返回值为零的时候是表示成功申请WSAStartup 26 { 27 return -1; 28 } 29 if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) // 检查版本号是否正确 30 { 31 WSACleanup( ); 32 return -1; 33 } 34 35 // 创建socket操作,建立流式套接字,返回套接字号sockClient 36 sockClient = socket(AF_INET, SOCK_STREAM, 0); 37 if(sockClient == INVALID_SOCKET) 38 { 39 cout << "Error at socket(): " << WSAGetLastError() << endl; 40 WSACleanup(); 41 return -1; 42 } 43 44 SOCKADDR_IN addrSrv; 45 addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本地回路地址是127.0.0.1; 46 addrSrv.sin_family = AF_INET; 47 addrSrv.sin_port = htons(6000); 48 49 // 将套接字sockClient与远程主机相连 50 // int connect( SOCKET s, const struct sockaddr* name, int namelen); 51 // 第一个参数:需要进行连接操作的套接字 52 // 第二个参数:设定所需要连接的地址信息 53 // 第三个参数:地址的长度 54 int con_id = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); 55 if(con_id == -1) 56 { 57 cout << "Error at connect(): " << WSAGetLastError() << endl; 58 WSACleanup(); 59 return -1; 60 } 61 62 cout << "本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。\n"; 63 64 bufferMutex = CreateSemaphore(NULL, 1, 1, NULL); 65 66 //创建发送信息与接收信息的线程 67 HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL); 68 HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, NULL, 0, NULL); 69 70 WaitForSingleObject(sendThread, INFINITE); // 等待线程结束 71 72 closesocket(sockClient); 73 CloseHandle(sendThread); 74 CloseHandle(receiveThread); 75 CloseHandle(bufferMutex); 76 WSACleanup(); // 终止对套接字库的使用 77 78 cout << "End linking..." << endl; 79 system("pause"); 80 return 0; 81 } 82 83 84 DWORD WINAPI SendMessageThread(LPVOID IpParameter) 85 { 86 while(1) 87 { 88 string talk; 89 getline(cin, talk); 90 WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用) 91 if("quit" == talk) 92 { 93 talk.push_back(‘\0‘); 94 send(sockClient, talk.c_str(), talk.size(), 0); 95 break; 96 } 97 98 cout << "I Say:(\"quit\"to exit):" << talk << endl; 99 send(sockClient, talk.c_str(), talk.size(), 0); // 发送信息 100 ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕) 101 } 102 return 0; 103 } 104 105 106 DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter) 107 { 108 while(1) 109 { 110 char recvBuf[300]; 111 recv(sockClient, recvBuf, 200, 0); 112 WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用) 113 if (recvBuf[0] == ‘q‘ && recvBuf[1] == ‘u‘ && recvBuf[2] == ‘i‘ && recvBuf[3] == ‘t‘ && recvBuf[4] == ‘\0‘) 114 { 115 closesocket(sockClient); 116 ReleaseSemaphore(bufferMutex, 1, NULL); // V操作(资源占用完毕) 117 cout << "Server has been closed ..." << endl; 118 exit(-1); 119 } 120 cout << "Server Says: " << recvBuf << endl; // 接收信息 121 ReleaseSemaphore(bufferMutex, 1, NULL); // V操作(资源占用完毕) 122 } 123 return 0; 124 }
以上是关于[socket编程] 一个服务器与多个客户端之间通信的主要内容,如果未能解决你的问题,请参考以下文章
C#使用Socket实现一个socket服务器与多个socket客户端通信