zoukankan      html  css  js  c++  java
  • socket的select模型【摘】

      

      套接字select模型是一种比较常用的IO模型。利用该模型可以使Windows socket应用程序可以同时管理多个套接字。

         使用select模型,可以使当执行操作的套接字满足可读可写条件时,给应用程序发送通知。收到这个通知后,应用程序再去调用相应的Windows socket API去执行函数调用。

         Select模型的核心是select函数。调用select函数检查当前各个套接字的状态。根据函数的返回值判断套接字的可读可写性。然后调用相应的Windows Sockets API完成数据的发送、接收等。

      利用select函数实现IO 管理。通过对select函数的调用,应用程序可以判断套接字是否存在数据、能否向该套接字写入数据。

     

         如:在调用recv函数之前,先调用select函数,如果系统没有可读数据那么select函数就会阻塞在这里。当系统存在可读或可写数据时,select函数返回,就可以调用recv函数接收数据了。

     

         可以看出使用select模型,需要两次调用函数。第一次调用select函数第二次socket API。使用该模式的好处是:可以等待多个套接字。

    int select (  
       Int nfds,//被忽略。传入0即可。  
       fd_set *readfds,//可读套接字集合。  
       fd_set *writefds,//可写套接字集合。  
       fd_set *exceptfds,//错误套接字集合。  
       const struct timeval*timeout);//select函数等待时间。
    /*参数列表
    int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
      
    fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
      
    fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
      
    fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
      
    struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
    第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
    第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
    第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
    */
    typedef struct fd_set  
    {  
         u_int fd_count;  //表示该集合套接字数量。最大为64
         socket fd_array[FD_SETSIZE];  //套接字数组
    }fd_set;

    timeval结构体用于定义select的等待时间

    structure timeval  
    {  
       long tv_sec;//秒。  
       long tv_usec;//毫秒。  
    };

      当timeval为空指针时,select会一直等待,直到有符合条件的套接字时才返回。

      当tv_sec和tv_usec之和为0时,无论是否有符合条件的套接字,select都会立即返回。

      当tv_sec和tv_usec之和为非0时,如果在等待的时间内有套接字满足条件,则该函数将返回符合条件的套接字。如果在等待的时间内没有套接字满足设置的条件,则select会在时间用完时返回,并且返回值为0。

    和select模型紧密结合的四个宏:

    FD_CLR( s,*set) 从队列set删除句柄s。

    FD_ISSET( s, *set) 检查句柄s是否存在与队列set中。

    FD_SET( s,*set )把句柄s添加到队列set中。

    FD_ZERO( *set ) 把set队列初始化成空队列。

    select选择模式倚赖于select函数,其思想就是让select函数对传入fd_set进行监视(fd_set中装有你的SOCKET句柄),如果没什么事发生select就将fd_set中的SOCKET清除。

    timeval outTime;
    outTime.tv = 1;   //设置等待时间为1s
    outTime.usec = 0; //毫秒
    fd_set fdread;
    while(true)
    {
       FD_ZERO(&fdread);
       FD_SET(sessionSock, &fdread) //sessionSock为之前创建的会话套接字
       select(0, &fdread, NULL, NULL, &outTime);
       if(FD_ISSET(sessionSock, &fdread))//判断套接字是否还在集合中
       {
          recv_cnt = recv(sessionSock, buf, bufSize, 0);
       }
       else
       {
          //没有数据写入,进行其他操作
       }
    }

    在开发Windows sockets应用程序时,通过下面的步骤,可以完成对套接字的可读写判断: 

        1:使用FD_ZERO初始化套接字集合。如FD_ZERO(&readfds);

        2:使用FD_SET将某套接字放到readfds内。如:    

          FD_SET(s,&readfds);

        3:以readfds为第二个参数调用select函数。select在返回时会返回所有fd_set集合中套接字的总个数,并对每个集合进行相应的更新。将满足条件的套接字放在相应的集合中。

        4:使用FD_ISSET判断s是否还在某个集合中。如:  

           FD_ISSET(s,&readfds);

         5:调用相应的Windows socket api 对某套接字进行操作。 

         select返回后会修改每个fd_set结构。删除不存在的或没有完成IO操作的套接字。这也正是在第四步中可以使用FD_ISSET来判断一个套接字是否仍在集合中的原因。

     一个服务器程序使用select模型管理套接字的例子

    SOCKET listenSocket; 
    SOCKET acceptSocket;  
    FD_SET socketSet;  
    FD_SET writeSet;  
    FD_SET readSet;  
      
      
      
    FD_ZERO(&socketSet);  
    FD_SET(listenSocket,&socketSet);  
    while(true)  
    {  
        FD_ZERO(&readSet);  
        FD_ZERO(&writeSet);  
        readSet=socketSet;  
        writeSet=socketSet;  
      
        //同时检查套接字的可读可写性。  
        int ret=select(0,&readSet,&writeSet,NULL,NULL);//为等待时间传入NULL,则永久等待。传入0立即返回。不要勿用。  
        if(ret==SOCKET_ERROR)  
        {  
            return false;  
        }  
        sockaddr_in addr;  
        int len=sizeof(addr);  
        //是否存在客户端的连接请求。  
        if(FD_ISSET(listenSocket,&readSet))//在readset中会返回已经调用过listen的套接字。  
        {  
            acceptSocket=accept(listenSocket,(sockaddr*)&addr,&len);  
            if(acceptSocket==INVALID_SOCKET)  
            {  
                return false;  
            }  
            else  
            {  
                FD_SET(acceptSocket,&socketSet);  
            }  
        }  
      
        for(int i=0;i<socketSet.fd_count;i++)  
        {  
            if(FD_ISSET(socketSet.fd_array[i],&readSet))  
            {  
                //调用recv,接收数据。  
            }  
            if(FD_ISSET(socketSet.fd_array[i]),&writeSet)  
            {  
                //调用send,发送数据。  
            }  
        }  
    } 

    http://blog.csdn.net/ithzhang/article/details/8363951

  • 相关阅读:
    angular4(2-1)angular脚手架引入第三方类库(jquery)
    angular4(1)angular脚手架
    vue-cli中的ESlint配置文件eslintrc.js详解
    咕着的题(慢慢补吧)
    图解Js event对象offsetX, clientX, pageX, screenX, layerX, x区别
    乐视手机H5项目总结
    解决ios下的微信打开的页面背景音乐无法自动播放
    html2canvas手机端模糊问题
    H5 canvas绘制出现模糊的问题
    hammer.js中文文档
  • 原文地址:https://www.cnblogs.com/yep3575/p/3344394.html
Copyright © 2011-2022 走看看