zoukankan      html  css  js  c++  java
  • socket select函数的详细讲解

    socket select函数的详细讲解

     

    原型

    int select(
    int nfds,
    fd_set* readfds,
    fd_set* writefds,
    fd_set* exceptfds,
    const struct timeval* timeout
    );

    nfds:本参数忽略,仅起到兼容作用。
       readfds:(可选)指针,指向一组等待可读性检查的套接口。
       writefds:(可选)指针,指向一组等待可写性检查的套接口。
       exceptfds:(可选)指针,指向一组等待错误检查的套接口。
       timeout:select()最多等待时间,对阻塞操作则为NULL。

    timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
    struct timeval
    {
    time_t tv_sec; //second 秒
    time_t tv_usec; //microsecond 微妙
    };

    注释:
       本函数用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。用fd_set结构来表示一组等待检查的套接口。在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。有一组宏可用于对fd_set的操作,这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。
       readfds参数标识等待可读性检查的套接口。如果该套接口正处于监听listen()状态,则若有连接请求到达,该套接口便被标识为可读,这样一个accept()调用保证可以无阻塞完成。对其他套接口而言,可读性意味着有排队数据供读取。或者对于SOCK_STREAM类型套接口来说,相对于该套接口的虚套接口已关闭,于是recv()或recvfrom()操作均能无阻塞完成。如果虚电路被“优雅地”中止,则recv()不读取数据立即返回;如果虚电路被强制复位,则recv()将以WSAECONNRESET错误立即返回。如果SO_OOBINLINE选项被设置,则将检查带外数据是否存在(参见setsockopt())。
       writefds参数标识等待可写性检查的套接口。如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立。如果套接口并未处于connect()调用中,可写性意味着send()和sendto()调用将无阻塞完成。〔但并未指出这个保证在多长时间内有效,特别是在多线程环境中〕。
       exceptfds参数标识等待带外数据存在性或意味错误条件检查的套接口。请注意如果设置了SO_OOBINLINE选项为假FALSE,则只能用这种方法来检查带外数据的存在与否。对于SO_STREAM类型套接口,远端造成的连接中止和KEEPALIVE错误都将被作为意味出错。如果套接口正在进行连接connect()(非阻塞方式),则连接试图的失败将会表现在exceptfds参数中。
       如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。
       在winsock.h头文件中共定义了四个宏来操作描述字集。FD_SETSIZE变量用于确定一个集合中最多有多少描述字(FD_SETSIZE缺省值为64,可在包含winsock.h前用#define FD_SETSIZE来改变该值)。对于内部表示,fd_set被表示成一个套接口的队列,最后一个有效元素的后续元素为INVAL_SOCKET。宏为:
       FD_CLR(s,*set):从集合set中删除描述字s。
       FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。
       FD_SET(s,*set):向集合添加描述字s。
       FD_ZERO(*set):将set初始化为空集NULL。
       timeout参数控制select()完成的时间。若timeout参数为空指针,则select()将一直阻塞到有一个描述字满足条件。否则的话,timeout指向一个timeval结构,其中指定了select()调用在返回前等待多长时间。如果timeval为{0,0},则select()立即返回,这可用于探询所选套接口的状态。如果处于这种状态,则select()调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它。举例来说,阻塞钩子函数不应被调用,且WINDOWS套接口实现不应yield。

    返回值:
       select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

    错误代码:
       WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
       WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
       WSAEINVAL:超时时间值非法。
       WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
       WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
       WSAENOTSOCK:描述字集合中包含有非套接口的元素。

    范例 :

    sock= socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in addr;      //告诉sock 应该再什么地方licence
    memset(&addr,0,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(11111);   //端口啦
    addr.sin_addr.s_addr=htonl(INADDR_ANY);        //在本机的所有ip上开始监听

    bind (sock,(sockaddr *)&addr,sizeof(addr));//bind....

    listen(sock,5);                   //最大5个队列

    SOCKET socka;                    //这个用来接受一个连接
    fd_set rfd;                     // 描述符集 这个将用来测试有没有一个可用的连接
    struct timeval timeout;

    FD_ZERO(&rfd);                     //总是这样先清空一个描述符集

    timeout.tv_sec=60;                //等下select用到这个
    timeout.tv_usec=0;

    u_long ul=1;

    ioctlsocket(sock,FIONBIO,&ul);    //用非阻塞的连接

    //现在开始用select
    FD_SET(sock,&rfd);    //把sock放入要测试的描述符集 就是说把sock放入了rfd里面 这样下一步调用select对rfd进行测试的时候就会测试sock了(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符, 
    if(select(sock+1,&rfd,0,0, &timeout)==0) 
    { //这个大括号接上面的,返回0那么就超过了timeout预定的时间

    //处理....

    }

    if(FD_ISSET(sock,&rfd))
    {      //有一个描述符准备好了

    socka=accept(sock,0,0);     //一个用来测试读 一个用来测试写

    FD_ZERO(&rfd);

    FD_ZERO(&wfd);

    FD_SET(socka,&rfd);//把socka放入读描述符集

    FD_SET(sockb,&rfd);//把sockb放入读描述符集

    FD_SET(socka,&wfd);把socka放入写描述符集

    FD_SET(sockb,&wfd);把sockb放入写描述符集

    if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0))      //测试这两个描述符集,永不超时 其中rfd只用来测试读 wfd只用来测试写

    {      //没有错误

    if(FD_ISSET(socka,&rfd))    //socka可读

    {...}

    if(FD_ISSET(sockb,&rfd)   //sockb可读

    {...}

    if(FD_ISSET(socka,&wfd) //socka 可写

    {...}

    if(FD_ISSET(sockb,&wfd) //sockb可写

    {...}

    }

    二、linux c中

    select(I/O多工机制)


    表头文件

    #i nclude<sys/time.h>
    #i nclude<sys/types.h>
    #i nclude<unistd.h>

    定义函数

    int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);

    函数说明

    select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
    FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
    FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
    FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
    FD_ZERO(fd_set *set); 用来清除描述词组set的全部位

    参数

    timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
    struct timeval
    {
    time_t tv_sec;
    time_t tv_usec;
    };

    返回值

    如果参数timeout设为NULL则表示select()没有timeout。

    错误代码

    执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。
    EBADF 文件描述词为无效的或该文件已关闭
    EINTR 此调用被信号所中断
    EINVAL 参数n 为负值。
    ENOMEM 核心内存不足

    范例

    常见的程序片段:fs_set readset;
    FD_ZERO(&readset);
    FD_SET(fd,&readset);
    select(fd+1,&readset,NULL,NULL,NULL);
    if(FD_ISSET(fd,readset){……}

    下面是linux环境下select的一个简单用法

    #i nclude <sys/time.h>
    #i nclude <stdio.h>
    #i nclude <sys/types.h>
    #i nclude <sys/stat.h>
    #i nclude <fcntl.h>
    #i nclude <assert.h>

    int main ()
    {
    int keyboard;
    int ret,i;
    char c;
    fd_set readfd;
    struct timeval timeout;
    keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
    assert(keyboard>0);
    while(1)
        {
    timeout.tv_sec=1;
    timeout.tv_usec=0;
    FD_ZERO(&readfd);
    FD_SET(keyboard,&readfd);
    ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
    if(FD_ISSET(keyboard,&readfd))
        {
          i=read(keyboard,&c,1);
              if('/n'==c)
              continue;
          printf("hehethe input is %c/n",c);
         
           if ('q'==c)
          break;
          }
    }
    }

  • 相关阅读:
    hdu 4027 Can you answer these queries? 线段树
    ZOJ1610 Count the Colors 线段树
    poj 2528 Mayor's posters 离散化 线段树
    hdu 1599 find the mincost route floyd求最小环
    POJ 2686 Traveling by Stagecoach 状压DP
    POJ 1990 MooFest 树状数组
    POJ 2955 Brackets 区间DP
    lightoj 1422 Halloween Costumes 区间DP
    模板 有源汇上下界最小流 loj117
    模板 有源汇上下界最大流 loj116
  • 原文地址:https://www.cnblogs.com/moonwind/p/4447567.html
Copyright © 2011-2022 走看看