[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编程] 一个服务器与多个客户端之间通信的主要内容,如果未能解决你的问题,请参考以下文章

Java 网络编程案例二:多态客户端与服务器之间的多次通信

Socket 编程

C#使用Socket实现一个socket服务器与多个socket客户端通信

如何在socket编程的Tcp连接中实现心跳协议

廖雪峰Java13网络编程-1Socket编程-3TCP多线程编程

socket编程与线程的结合