• Windows Socket 编程_单个服务器对多个客户端简单通讯 .


    单个服务器对多个客户端程序:

    一。简要说明

    二。查看效果

    三。编写思路

    四。程序源代码

    五。存在问题

    一。简要说明:

     程序名为: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 << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车.\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”时不能正常退出。

    from:http://blog.csdn.net/neicole/article/details/7539444

  • 相关阅读:
    个人永久性免费-Excel催化剂功能第31波-数量金额分组凑数功能,财务表哥表姐最爱
    个人永久性免费-Excel催化剂功能第30波-工作表快捷操作(批量创建、命名、排序、工作表目录)
    个人永久性免费-Excel催化剂功能第29波-追加中国特色的中文相关自定义函数
    发现用System.Net.Mail发邮件(代码附后),附件稍微大一点就会造成程序假死. 有没有什么简单的解决办法呢? 多谢!!
    上传文件 获取文件名 --360浏览器
    js中的json对象和字符串之间的转化
    chosen.jquery.js
    select 后台获取text 并根据text 设置value选中项
    iframe中有ajax,设置iframe自适应高度
    charme浏览器 jquery1.9.1min.js 报脚本错误 无jquery.min.map 文件
  • 原文地址:https://www.cnblogs.com/lidabo/p/2812235.html
Copyright © 2020-2023  润新知