博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于select模型的tcp服务器------一个服务器如何与多个客户端进行通信
阅读量:4104 次
发布时间:2019-05-25

本文共 3926 字,大约阅读时间需要 13 分钟。

               

      很多时候, 服务器都需要同时与多个客户端进行通信, 服务嘛, 就是这样。 下面, 我们用select模型来简要模拟一下这种情形。代码是最好的解释, 所以, 还是上代码吧:

      服务端程序:

#include 
#include
   #pragma comment(lib, "ws2_32.lib")int totalSockets = 0; // socket的总数SOCKET socketArray[100]; // socket组成的数组, 假设最多有100个socket吧// 日志打印void log(const char *pStr){ FILE *fp = fopen("log.txt", "a"); fprintf(fp, "log:%s\n", pStr); fclose(fp);}// 创建socketvoid addToSocketArr(SOCKET s)   {    socketArray[totalSockets] = s;           totalSockets++; }// 启动服务器int main(){ log("into main"); // 网络初始化 WSADATA   wsaData;    WSAStartup(MAKEWORD(1, 1), &wsaData); // 创建socket    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);    addToSocketArr(listenSocket);        // 服务地信息 SOCKADDR_IN   srvAddr;    srvAddr.sin_family = AF_INET;       srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);       srvAddr.sin_port = htons(8888);    // 绑定    bind(listenSocket, (SOCKADDR*)&srvAddr, sizeof(srvAddr));    // 监听    listen(listenSocket, 5);    // 设置socket为非阻塞模式    unsigned long nonBlock = 1;       ioctlsocket(listenSocket, FIONBIO, &nonBlock);   while(1)       {  // 读集, 要记得清零初始化  FD_SET   readSet;        FD_ZERO(&readSet);     // 将每个socket都塞入读集, 便于让内核来监测这些socket  int i = 0;        for(i = 0; i < totalSockets; i++)     {               FD_SET(socketArray[i], &readSet);      }  // 应用程序通知内核来监测读集中的socket, 最后的NULL表示超时时间无限长  int total = select(0, &readSet, NULL, NULL, NULL);          // 我们不考虑select失败, 那么程序到这里, 说明读集中必有socket处于"就绪状态"        for(i = 0; i < totalSockets; i++)     {   char szTmp[20] = {
0};   sprintf(szTmp, "%d", i);   log(szTmp);   if(socketArray[i] == listenSocket)  // 对监听的socket进行判断   {    log("socketArray[i] == listenSocket");    if(FD_ISSET(listenSocket, &readSet)) // 如果该socket在可读集中, 则表明有客户端来连接    {     log("listenSocket, socketArray[i] == listenSocket");     // 接收来自于客户端的connect请求     SOCKADDR_IN addrClient;       int len = sizeof(SOCKADDR);     SOCKET acceptSocket = 0;     acceptSocket = accept(listenSocket,(SOCKADDR*)&addrClient, &len);      // 设置为非阻塞模式     nonBlock = 1;        ioctlsocket(acceptSocket, FIONBIO, &nonBlock);     // 添加到socket数组中     addToSocketArr(acceptSocket);    }    continue;   }   // 注意:上面的listenSocket是不负责通信的, 下面的一些socket都是负责通信的socket   // 如果通信socket处于读就绪状态   if (FD_ISSET(socketArray[i], &readSet))               {    log("to receive");    char szRecvBuf[1024] = {
0};    recv(socketArray[i], szRecvBuf, sizeof(szRecvBuf) - 1, 0);       printf("socketArray[i] is %d, %s\n", socketArray[i], szRecvBuf);   }  }         } // 省略了关闭socket等后续操作 return 0;}
      启动服务端。

      

      再看客户端程序:

#include 
#include
#pragma comment(lib, "ws2_32.lib")int main(){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1);  WSAStartup( wVersionRequested, &wsaData ); SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888);  connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));  while(1) {  char szSendBuf[100] = {
0};  scanf("%s", szSendBuf);  send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0); } closesocket(sockClient); WSACleanup(); return 0;}
        在此处, 我们编译并链接, 然后连续运行8次客户端(不要关闭), 也就产生了8个客户端进程, 并在8个客户端进程中依次输入1, 2, 3, 4, 5, 6, 7, 8后分别按Enter发送, 然后在第一个客户端进程中再次输入111, 并按Enter发送。 我们看看服务端的结果:

socketArray[i] is 136, 1

socketArray[i] is 152, 2
socketArray[i] is 164, 3
socketArray[i] is 176, 4
socketArray[i] is 188, 5
socketArray[i] is 200, 6
socketArray[i] is 212, 7
socketArray[i] is 224, 8
socketArray[i] is 136, 111

       可以看到, 服务端可以同时与各个客户端进行通信, 而且还不会混淆他们。 另外,从log.txt日中中可以看出, 尽管有8个客户端, 但服务端的监听socket是唯一的。有一个小小的问题值得我们注意: 在服务端, 我们listen函数的第二个参数是5, 很多人会认为是服务端最多允许5个客户端同时连接, 其实, 不是酱紫的。看看, 我们实战中就有8个客户端同时连接上了呢, 我们后续会对listen的第二个参数的具体含义进行更加详细的讨论。

      最后要说明的是, 上述程序还是比较简单的, 没有考虑很多异常的情况, 比如, 某个客户端突然退出对服务器的影响。 如果以后玩程序的时候需要考虑更复杂的情况, 我们再完善。

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!

转载地址:http://oscsi.baihongyu.com/

你可能感兴趣的文章
中兴F460光猫破解超级管理员
查看>>
vmtools官方下载地址
查看>>
提取vmware tools的windows.iso文件方法
查看>>
大量远控源码
查看>>
常见的Radius服务器
查看>>
性价比比较高的PHY芯片
查看>>
Mac下使用Xquartz连接CentOS的Xdm服务器
查看>>
远程管理MAC OS
查看>>
变色龙常用参数的意义说明
查看>>
Xen常用的基本命令
查看>>
微软正式提供Visual Studio 2013正式版下载(附直接链接汇总)
查看>>
EFI/GPT探索(为何win7分区时创建100M隐藏分区)
查看>>
Chemeleon如何隐藏非系统的NTFS分区
查看>>
Mac下挂载EFI分区
查看>>
解决Win8.1无法关机问题
查看>>
解决Windows和Mac系统时间不同步的方法
查看>>
英特尔的VT-d技术是什么?
查看>>
三层交换机与路由器的区别
查看>>
google编码规范
查看>>
QTextCodec中的setCodecForTr等终于消失了 (Qt5)
查看>>