Winsock—I/O模型之选择模型(一)

 Winsock中提供了一些I/O模型帮助应用程序以异步方式在一个或多个套接字上管理I/O。

  这样的I/O模型有六种:阻塞(blocking)模型,选择(select)模型,WSAAsyncSelect模型,WSAEventSelect模型,重叠(overlapped)模型,完成端口(completion port)模型。

  选择模型:

  目的:允许想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字。

  一、select函数
             select函数可以确定一个或者多个套接字的状态。如果套接字上没有网络事件发生,便进入等待状态,以便执行同步I/O。

1   int select(int nfds,         //仅是为了与套接字兼容,忽略
2     fd_set* readfds,         //指向一个套接字集合,检查其可读性
3     fd_set* writefds,         //检查一个套接字集合,检查其可写性
4     fd_set* exceptfds       //指向一个套接字集合,检查错误
5     const struct timeval* timeout    //指定函数最长等待时间,如果为NULL,则等待时间为无限长
6 );  

       函数调用成功,返回发生网络事件的所有的套接字数量的总和。如果超过了时间限制,返回0,失败则返回SOCKET_ERROR。

  1、套接字集合
        fd_set结构可以把多个套接字连接在一起,形成一个套接字集合。select函数可以测试这个集合中哪些套接字有事件发生。

    WINSOCK2.h中的定义如下:

1 typedef struct fd_set{
2     u_int fd_count;      //下面数组的大小
3     SOCKET fd_array[FD_SETSIZE];    //套接字句柄数组       
4 }

    WINSOCK中定义的4个操作fd_set套接字集合的宏:

1     FD_ZERO(*set);     //初始化set为空集合。集合使用前应该总是清空。
2     FD_CLR(s,*set);    //从set移除套接字s
3     FD_ISSET(s,*set)    //检查s是不是set的成员,如果是返回TRUE.
4     FD_SET(s,*set)     //添加套接字到集合

   2、设置超时
       最后的参数timeout是timeval结构的指针,它指定了select函数等待的最长的时间。如果设为NULL,select将会无限阻塞,直到有网络事件发生。

    timeval结构定义如下:

1  typedef struct timeval
2 {
3      long tv_sec;      //指示等待多少秒
4      long tv_usec;     //指示等待多少毫秒
5 }timeval;

  服务器端代码:

 1  2 
 3 CInitSock theSock;  //初始化Winsock库
 4 int main()
 5 {
 6 USHORT nport = 4567;   //此服务器监听的端口号
 7     SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 8     sockaddr_in sin;
 9     sin.sin_family = AF_INET;
10     sin.sin_port = htons(nPort);
11     sin.sin_addr.S_un.S_addr = INADDR_ANY;                 //绑定监听套接字到本地
12     if(bind(sListen,(sockaddr*)&sin,sizeof(sin)==SOCKET_ERROR)      //绑定套接字失败
13     {
14         printf("Failed bind()n");
15         return -1;
16      }
17      listen(sListen,5);                                   //进入监听模式
18      fd_set fdSocket;                                    //创建套接字集合
19      FD_ZERO(&fdSocket);                                //初始化套接字结合
20      FD_SET(&sListen,&fdSocket);                       //将监听套接字添加进套接字集合中
21      while(TRUE)
22      {
23          fd_set fdRead = fdSocket;                        //将fdSocket集合拷贝到fdRead
24          int nRet = select(0,&fdRead,NULL,NULL,NULL);    //当有事件发生发生时,select函数将移除fdRead集合中没有未决I/O操作的套接字句柄,然后返回。
25          if(nRet>0)
26          {
27            //通过将原来的fdSocket集合与select处理过的fdRead集合比较,确定有哪些套接字有未决I/O,进一步处理这些I/O。
28            for(int i=0;i<(int)fdSocket.fd_count;i++)
29       {
30           if(FD_ISSET(fdSocket.fd_array[i],&fdRead))
31       {
32           if(fdSocket.fd_count<FD_SETSIZE)
33       {
34            sockaddr_in addrRemote;
35            int nAddrLen = sizeof(addrRemote);
36            SOCKET sNew = accept(sListen,(SOCKADDR*)&addrRemote,&nAddrlen);
37            FD_SET(sNew,&fdSocket);
38            printf("接收到连接(%s)
",inet_ntoa(addrRemote.sin_addr));
39       }
40             else 
41              {
42            printf("Too Much Connections!");
43            continue;
44               }
45       }
46              else
47              {
48             char szText[256];
49             int nRecv = recv(fdSocket.fd_array[i],szText,strlen(szText),0);               //接收
50               if(nRecv>0)
51               {
52                    szText[nRecv] = '';
53                    printf("接收到数据:%s
",szText);
54                }
55                else
56                {
57                  closesocket(fdRead.fd_array[i]);
58                  FD_CLR(fdSocket.fd_array[i],&fdSocket);
59                 }
60               }
61              }
62             }
63           }
64   else
65   {
66     printf("Failed select()
");
67     break;
68   }
69   }
70   return 0;
71 }
原文地址:https://www.cnblogs.com/suvllian/p/5470156.html