单个服务器对多个客户端程序:
一。简要说明
二。查看效果
三。编写思路
四。程序源代码
五。存在问题
一。简要说明:
程序名为:TcpSocketOneServerToMulClient
程序功能:实现单个服务器对多个客户端通讯功能的小程序。
PS: 这是继上次简单的 Tcp Windows Socket 编程后的再一程序,程序实现依然不是很严谨,还待完善~
二。查看效果:
三。编写思路:
由上一次的程序思路来看,如果想实现单个服务器对多个客户端程序的通讯的话,这次程序编写尝试从多线程的角度来考虑问题:
在服务器的实现中:可以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 << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车. "; 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(" CreatThread AnswerThread() failed. "); 68 } 69 else{ 70 printf(" Create Receive Client Thread OK. "); 71 } 72 ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕) 73 } 74 75 WaitForSingleObject(sendThread, INFINITE); // 等待线程结束 76 CloseHandle(sendThread); 77 CloseHandle(bufferMutex); 78 WSACleanup(); // 终止对套接字库的使用 79 printf(" "); 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(" "); 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] == '