• [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 << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车.
    ";
     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('');
    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] == '')
    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 << "本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。
    ";
     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('');
     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] == '')
    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 }
  • 相关阅读:
    oracle常用hint的用法
    浅谈reverse函数与django哲学
    javascript console
    python os.path模块
    删除列表元素
    Python模块学习 pickle, cPickle 对象序列化/反序列化
    Python中zip()函数用法举例
    Python 字符串方法详解
    常用正则验证
    python中下划线的用法
  • 原文地址:https://www.cnblogs.com/lca1826/p/6662438.html
Copyright © 2020-2023  润新知