• winsock编程select模型


    winsock编程select模型

      网络服务端连接数量过多时,为每一个连接申请一个线程会让机器性能急剧下降(大多说是因为线程在用户态和内核态之间切换会占用大量的CPU时间片)。为了解决多线程带来的性能下降问题,windows提供了5种网络编程模型。这其中,最简单的就是select模型。

      select模型的基本思想是,同时管理一组socket。每次调用select,系统会检查这一组socket的状态,并返回可读、可写、异常socket的信息。有了socket信息,便可进行recv、send操作。为了不阻塞主线程,对一组socket的轮询操作仅需要开辟一个线程即可。当然,这一组socket的大小不能无限制增长,windows定义了select调用参数socket数组的最大长度为64。如果当前socket数组的长度大于64,需要将该数组划分成长度小于等于64的小数组,分别进行轮询。

      select模型主要用到以下函数和宏:

    1:select函数

    Description:The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O。

    1 int select(
    2   __in          int nfds,
    3   __in_out      fd_set* readfds,
    4   __in_out      fd_set* writefds,
    5   __in_out      fd_set* exceptfds,
    6   __in          const struct timeval* timeout
    7 );

    Parameters

    nfds

    Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.该参数会被忽略掉

    readfds

    Optional pointer to a set of sockets to be checked for readability.

    writefds

    Optional pointer to a set of sockets to be checked for writability.

    exceptfds

    Optional pointer to a set of sockets to be checked for errors.

    timeout

    Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

    Return Value

      The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

      select返回值0,时间超时。返回值大于0,表示传入参数fd_set中中已就绪的socket数量。

    2:ioctlsocket

    Descriptio:The ioctlsocket function controls the I/O mode of a socket.

    1 int ioctlsocket(
    2   __in          SOCKET s,
    3   __in          long cmd,
    4   __in_out      u_long* argp
    5 );

    Parameters

    s

    Descriptor identifying a socket.

    cmd

    Command to perform on the socket s.

    argp

    Pointer to a parameter for cmd.

    Return Value

      Upon successful completion, the ioctlsocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

      ioctlsocket用来设置socket的IO传输模式,返回值0,表示设置成功。cmd参数和argp参数需要配合使用,常用的参数值组合如下:

      A:cmd:FIONBIO    argp为0表示设置soclet为阻塞模式,非0表示设置socket为非阻塞模式。

      B:cmd:FIONREAD   用了确定socket的输入缓冲区内剩余的可读字节数,用argp接收该数值。

      C:cmd:SIOCATMARK  用来确定out of band (OOB)数据是否已读。

      ioctlsocket 设置socket味非阻塞模式的代码如下:

    1 u_long iMode = 0;
    2 ioctlsocket(m_socket, FIONBIO, &iMode);

    3:fd_set结构体和相关宏

      select传入的参数为fd_set结构体,fd_set定义如下:

    1 typedef struct fd_set {  
    2 u_int fd_count;  
    3 SOCKET fd_array[FD_SETSIZE];
    4 } fd_set;

      FD_SETSIZE的值为64,也就是说windows已经把数组fd_array的长度定义好,不要超过该长度。下面是操作fd_set结构体的几个宏:

      FD_ISSET宏:用于判断socket是否在fd_set中,是返回true,否则返回false。

    #define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set))

       FD_ZERO宏:把fd_set内部的count赋值为0.

    #define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)

       FD_CLR宏:在fd_set数组中清除一个socket

     1 #define FD_CLR(fd, set) do { 
     2     u_int __i; 
     3     for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { 
     4         if (((fd_set FAR *)(set))->fd_array[__i] == fd) { 
     5             while (__i < ((fd_set FAR *)(set))->fd_count-1) { 
     6                 ((fd_set FAR *)(set))->fd_array[__i] = 
     7                     ((fd_set FAR *)(set))->fd_array[__i+1]; 
     8                 __i++; 
     9             } 
    10             ((fd_set FAR *)(set))->fd_count--; 
    11             break; 
    12         } 
    13     } 
    14 } while(0)

      FD_SET宏:在fd_set数组中添加一个socket

     1 #define FD_SET(fd, set) do { 
     2     u_int __i; 
     3     for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { 
     4         if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { 
     5             break; 
     6         } 
     7     } 
     8     if (__i == ((fd_set FAR *)(set))->fd_count) { 
     9         if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { 
    10             ((fd_set FAR *)(set))->fd_array[__i] = (fd); 
    11             ((fd_set FAR *)(set))->fd_count++; 
    12         } 
    13     } 
    14 } while(0)

       timeval结构体,select超时参数

    1 /* Structure used in select() call, taken from the BSD file sys/time.h.*/
    2 struct timeval {
    3         long    tv_sec;         /* seconds */
    4         long    tv_usec;        /* and microseconds */
    5 };

     4:例子程序

       select模式主要用到的就是select和ioctlsocket函数,下面是例子程序

      1 void main()
      2 {
      3     WSAData wsa;
      4     if (WSAStartup(MAKEWORD(1,1),&wsa) != 0)
      5     {
      6         WSACleanup();
      7         return 0;
      8     }
      9     SOCKET listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     10     do 
     11     {
     12         if (listensocket == INVALID_SOCKET)
     13             break;
     14         sockaddr_in service;
     15         service.sin_family = AF_INET;
     16         service.sin_port = htons(8828);
     17         service.sin_addr.s_addr = INADDR_ANY;
     18         if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
     19         {
     20             cout<<"bind error,error code "<<WSAGetLastError()<<endl;
     21             break;
     22         }
     23         if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR)
     24         {
     25             cout<<"listen error,error code "<<WSAGetLastError()<<endl;
     26             break;
     27         }
     28         //以下是select相关代码
     29         //此处把listensocket设置为非阻塞模式
     30         u_long unblock = 1;    //非0为非阻塞模式
     31         if (ioctlsocket(listensocket,FIONBIO,&unblock) == SOCKET_ERROR)
     32         {
     33             cout<<"ioctlsocket(listensocket) error,error code "<<WSAGetLastError()<<endl;
     34             break;
     35         }
     36         fd_set m_readset;
     37         fd_set m_writeset;
     38         FD_SET(listensocket,&m_readset);
     39         SOCKET socks[64];
     40         socks[0] = listensocket;
     41         int TotalSockets = 1;
     42         while (1)
     43         {
     44             fd_set readset = m_readset;
     45             fd_set writeset = m_writeset;
     46             timeval tv;
     47             tv.tv_sec = 1;
     48             tv.tv_usec = 0;
     49             int res = select(0,&readset,&writeset,NULL,&tv);
     50             if (res == SOCKET_ERROR)
     51             {
     52                 cout<<"select error,error code "<<WSAGetLastError()<<endl;
     53                 break;
     54             }
     55             if (res == 0)
     56             {
     57                 continue;//表示当前无状态可控的socket
     58             }
     59             int tempTotalSockets = TotalSockets;
     60             for (int i=0;i<TotalSockets;i++)
     61             {
     62                 if (FD_ISSET(socks[i],&readset))
     63                 {
     64                     if (socks[i] == listensocket)
     65                     {
     66                         //listensocket可读,表示连接到达
     67                         SOCKET acp = accept(listensocket,NULL,NULL);
     68                         if (acp == INVALID_SOCKET)
     69                         {
     70                             cout<<"accept error,error code "<<WSAGetLastError()<<endl;
     71                             break;
     72                         }
     73                         //设置新到达socket为非阻塞模式,并加入socks以及fdset
     74                         if (ioctlsocket(acp,FIONBIO,&unblock) == SOCKET_ERROR)
     75                         {
     76                             cout<<"ioctlsocket(acp) error,error code "<<WSAGetLastError()<<endl;
     77                             break;
     78                         }
     79                         FD_SET(acp,m_readset);
     80                         FD_SET(acp,m_writeset);
     81                         socks[tempTotalSockets++] = acp;
     82                     }
     83                     else
     84                     {
     85                         char buf[1024];
     86                         int len = recv(socks[i],buf,1024,0);
     87                         if (len == 0)
     88                         {
     89                             cout<<"connection has been closed "<<endl;
     90                             break;
     91                         }
     92                         else if (len == SOCKET_ERROR)
     93                         {
     94                             cout<<"recv error,error code "<<WSAGetLastError()<<endl;
     95                             break;
     96                         }
     97                         else
     98                         {
     99                             char* outbuf = new char[len+1];
    100                             memcpy(outbuf,buf,len);
    101                             outbuf[len] = 0;
    102                             cout<<"recv data,"<<outbuf<<endl;
    103                             delete outbuf;
    104                         }
    105 
    106                     }
    107                 }
    108                 else if (FD_ISSET(socks[i],&writeset))
    109                 {
    110                     std::string str = "send data!";
    111                     if (send(socks[i],str.c_str(),str.length(),0) == SOCKET_ERROR)
    112                     {
    113                         cout<<"send error,error code "<<WSAGetLastError()<<endl;
    114                         break;
    115                     }
    116                 }
    117             }
    118             TotalSockets = tempTotalSockets;
    119         }
    120         
    121     } while (0);
    122     closesocket(listensocket);
    123     WSACleanup();
    124 }

      

  • 相关阅读:
    大话设计模式之代理模式
    大话设计模式之装饰者模式
    策略模式与简单工厂模式
    一个简单的使用Quartz和Oozie调度作业给大数据计算平台执行
    oozie JAVA Client 编程提交作业
    HashMap分析及散列的冲突处理
    cmp排序hdoj 1106排序
    定义member【C++】cstddef中4个定义
    目录启动CXF启动报告LinkageError异常以及Java的endorsed机制
    算法代码[置顶] 机器学习实战之KNN算法详解
  • 原文地址:https://www.cnblogs.com/hgwang/p/6086908.html
Copyright © 2020-2023  润新知