Winsock select编程模型

  1、winsock的两种I/O模式

        Windows  Socket套接字可以在两种模式下进行I/O操作:阻塞模式和非阻塞模式。

       在阻塞模式下,执行I/O操作的函数在操作完成之前会一直等待,不会将这种控制权返回给程序,这样任一线程在某一时刻只能执行一个I/O操作。套接字在创建时默认为阻塞模式。在非阻塞模式下,执行I/O操作的Winsock函数会立即返回并交出控制权,之后再通过其他机制获得操作完成的通知。

   2、Windows  sockets下的编程模型

            (1)select模式:在阻塞模式下,使用select()函数来确定一个或多个套接字的状态,如判断套接字上是否有数据可读,或者能否向一个套接字写入数据,防止应用程序    在套接字处于锁定模式时,调用recv(或send)从没有数据的套接字上接收数据,被迫进入阻塞状态。其函数原型为:

     int select (

                         IN int nfds,                         //0,无意义

                         IN OUT fd_set* readfds,     //检查可读性

                         IN OUT fd_set* writefds,     //检查可写性

                         IN OUT fd_set* exceptfds,  //带外数据

                         IN const struct timeval* timeout);    //函数的返回时间,指针NULL一直等待

         请求状态查询的套接字集合由一个fd_set结构给出,在函数返回时此结构更新以反映那些满足特定条件的套接字的子集,同时select函数返回满足条件的套接字的数目。头文件winsock2.h中定义了一组操作fd_set结构的宏,他们分别是:

                     FD_CLR(s,*set)                                   从集合中删除描述符s

                     FD_ISSET(s,*set)                             如果s是集合中的一个元素,则返回非零,否则返回0;

                     FD_SET(s,*set)                                       将描述符s加入集合

                     FD_ZERO(*set)                                   将集合初始化为空集

                      FD_SETSIZE                                           常量,表示最多可容纳套接字的数量,默认为64

        select模式工作流程:

                           【1】 用FD_ZERO宏来初始化fd_set

                                 可以初始化的有select函数的第二三四个参数。

                           【2】用FD_SET宏来将套接字句柄分配给相应的fd_set

                                  例如如果想要检查一个套接字有数据需要接收,可以用FD_SET宏把套接接字句柄加入可读性检查队列中

                           【3】调用select函数。

                                  如果该套接字没有数据需要接收,select函数会把该套接字从可读性检查队列中删除掉,

                            【4】用FD_ISSET对套接字句柄进行检查。

                                   如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么说明马上可以进行相应的IO操 作。

                            【5】对满足条件的套接字执行一定的操作。

      贴一段别人的代码实例:

      #include <winsock2.h>     
    #include <stdio.h>     
      
    #define PORT  8000     
      
    #define MSGSIZE  1024     
      
    #pragma comment(lib, "ws2_32.lib")     
      
    int TotalConn = 0;      
      
    SOCKET SocketArr[FD_SETSIZE];      
      
    DWORD WINAPI WorkerThread(LPVOID lpParam);     
    int CALLBACK ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * g,DWORD dwCallbackData);  
      
      
    int main(int argc, char* argv[])     
    {     
     WSADATA wsaData;     
     SOCKET sListen, sClient;     
     SOCKADDR_IN local, client;     
     int iAddrSize = sizeof(SOCKADDR_IN);     
     DWORD dwThreadId;     
     // Initialize windows socket library     
     WSAStartup(0x0202, &wsaData);     
     // Create listening socket     
     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);     
     // Bind     
      
     local.sin_family = AF_INET;     
     local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);     
     local.sin_port = htons(PORT);     
     bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));     
      
     // Listen     
      
     listen(sListen, 3);     
      
     // Create worker thread     
      
     CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);       
      
     while (TRUE)      
     {       
      sClient = WSAAccept(sListen, (sockaddr*)&client, &iAddrSize, ConditionFunc, 0);     
      SocketArr[TotalConn++] = sClient;    
     }     
     return 0;     
    }     
      
    DWORD WINAPI WorkerThread(LPVOID lpParam)     
    {     
     int i;     
     fd_set fdread;     
     int ret;     
     struct timeval tv = {1, 0};     
     char szMessage[MSGSIZE];     
     while (TRUE)      
     {     
      FD_ZERO(&fdread);   //1清空队列  

      for (i = 0; i < TotalConn; i++)      
      {     
       FD_SET(SocketArr[i], &fdread);   //2将要检查的套接口加入队列  
      }     
      
      // We only care read event     
      ret = select(0, &fdread, NULL, NULL, &tv);   //3查询满足要求的套接字,不满足要求,出队  
      if (ret == 0)      
      {     
       // Time expired     
       continue;     
      }     
      
      for (i = 0; i < TotalConn1; i++)      
      {     
       if (FD_ISSET(SocketArr[i], &fdread))    //4.是否依然在队列  
       {     
        // A read event happened on SocketArr     
      
        ret = recv(SocketArr[i], szMessage, MSGSIZE, 0);     
        if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))      
        {     
        // Client socket closed     
         printf("1:Client socket %d closed.\n", SocketArr[i]);     
         closesocket(SocketArr[i]);     
         if (i < TotalConn-1)      
         {     
          SocketArr[i--] = SocketArr[--TotalConn];  //删除关闭的套接字   
         }     
        }      
        else      
        {     
         // We reveived a message from client     
         szMessage[ret] = '\0';     
         send(SocketArr[i], szMessage, strlen(szMessage), 0);     
        }     
       }     
      }    
     }     
    }   
      
    int CALLBACK ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * g,DWORD dwCallbackData)  
    {  
     if (TotalConn< FD_SETSIZE)  
      return CF_ACCEPT;  
     else  
      return CF_REJECT;  
    }  


             

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用winsock库的select函数的示例代码: ```c++ #include <winsock2.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库文件 int main() { WSADATA wsaData; int iRet = 0; iRet = WSAStartup(MAKEWORD(2,2), &wsaData); //初始化winsock库 if(iRet != NO_ERROR) { printf("WSAStartup() failed with error: %d\n", iRet); return 1; } SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建TCP socket if(sockServer == INVALID_SOCKET) { printf("socket() failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } sockaddr_in addrSrv; addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); addrSrv.sin_addr.s_addr = htonl(INADDR_ANY); iRet = bind(sockServer, (const sockaddr *)&addrSrv, sizeof(addrSrv)); //绑定socket到指定IP和端口号 if(iRet == SOCKET_ERROR) { printf("bind() failed with error: %ld\n", WSAGetLastError()); closesocket(sockServer); WSACleanup(); return 1; } iRet = listen(sockServer, 5); //开始监听 if(iRet == SOCKET_ERROR) { printf("listen() failed with error: %ld\n", WSAGetLastError()); closesocket(sockServer); WSACleanup(); return 1; } fd_set fdRead; //定义读文件描述符集合 FD_ZERO(&fdRead); //清空读文件描述符集合 FD_SET(sockServer, &fdRead); //将监听socket加入读文件描述符集合 while(true) { fd_set fdReadBackup = fdRead; //备份读文件描述符集合,因为select会改变集合内容 iRet = select(0, &fdReadBackup, NULL, NULL, NULL); //等待读文件描述符集合有数据可读 if(iRet == SOCKET_ERROR) { printf("select() failed with error: %ld\n", WSAGetLastError()); break; } else if(iRet > 0) { if(FD_ISSET(sockServer, &fdReadBackup)) //监听socket有数据可读,表示有新连接请求 { sockaddr_in addrClient; int addrClientLen = sizeof(addrClient); SOCKET sockConn = accept(sockServer, (sockaddr *)&addrClient, &addrClientLen); //接受连接请求 if(sockConn == INVALID_SOCKET) { printf("accept() failed with error: %ld\n", WSAGetLastError()); break; } printf("New client connected: %s:%d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port)); FD_SET(sockConn, &fdRead); //将连接socket加入读文件描述符集合 } else //连接socket有数据可读,表示客户端发送了数据 { for(int i = 0; i < fdReadBackup.fd_count; i++) //遍历读文件描述符集合中的所有连接socket { SOCKET sockConn = fdReadBackup.fd_array[i]; if(sockConn != sockServer) //排除监听socket { char szBuf[1024] = {0}; iRet = recv(sockConn, szBuf, sizeof(szBuf), 0); //接收数据 if(iRet == SOCKET_ERROR) { printf("recv() failed with error: %ld\n", WSAGetLastError()); break; } else if(iRet == 0) //对方关闭连接 { printf("Client disconnected.\n"); closesocket(sockConn); FD_CLR(sockConn, &fdRead); //从读文件描述符集合中移除连接socket } else //正常接收到数据 { printf("Received data from client: %s\n", szBuf); send(sockConn, szBuf, iRet, 0); //回复相同的数据 } } } } } } closesocket(sockServer); WSACleanup(); return 0; } ``` 该代码实现了一个简单的TCP服务器,通过select函数实现了多路复用,可以同时处理多个客户端连接。在主循环中,使用select函数等待读文件描述符集合中的socket有数据可读,当有新连接请求时,将连接socket加入读文件描述符集合,当连接socket有数据可读时,接收数据并回复相同的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值