在非阻塞下select函数的使用

在非阻塞下select函数的使用

函数原型
int select(nfds, readfds, writefds, exceptfds, timeout)
    int nfds;
    fd_set *readfds, *writefds, *exceptfds;
    struct timeval *timeout;
ndfs:select 监视各文件中的最大文件号加一。
比如有两个文件描述符fd1,fd2 则
ndfs = fd1 >  fd2 ? fd1+1 : fd2+1;
readfds:select 监视的可读文件句柄集合。
writefds: select 监视的可写文件句柄集合。
exceptfds:select 监视的异常文件句柄集合。
timeout:本次 select()的超时结束时间。
超时时间设置
第一,若将 NULL 以形参传入,即不传入时间结构,就是将 select 置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout 的值大于0,这就是等待的超时时间,即 select 在 timeout 时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回


select函数的返回值
负值:select 错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件

调用select()以后,如果返回正值,需判断是读/写/异常
使用FD_ISSET(int fd, fd_set *)判断

以阻塞写函数为例进行具体分析
static ssize_t write_nonblock(int fd, void *buf, size_t count)
{
    size_t nc=0; int err=0;

    /* simulate a 10 seconds timeout */
    struct timeval to;
    int    nr_tries=10;

    fd_set wfds, efds;//write/error fd
    if (fd < 0 || !buf)
        return -1;
   

    while (count > 0) {

        err = write(fd, buf, count);
        if (err > 0) {
            buf += err;
            nc  += err;
            count -= err;
            continue;
        }
        if (err < 0 && errno != EAGAIN)
        /* Error occurs */
            break;
        if (--nr_tries == 0)
                                                                                /* Timeout:                                                                        * Didn't send out anything in the continuous 10 seconds
          */
            break;
        //在调用select()函数之前,必须清0,文件句柄集合
        //清除写句柄集合
        FD_ZERO(&wfds);
            //清除错误句柄集合
        FD_ZERO(&efds);
        //初始化句柄集合
                                                                                         
                FD_SET(fd, &wfds);
                                                                                      FD_SET(fd, &efds);
        //延迟1s
        to.tv_sec  = 1;
                                                                                             
                to.tv_usec = 0;

                                                                                                                     
                err = select(fd+1, NULL, &wfds, &efds, &to);
                                                                                /
                /*select会监视句柄集合在1s中是否有变化,然后返回*/
        //若 err==0,则继续掩延时,直到10s超时
                                                                                          
               if (err > 0) {


            //句柄有变化,判断是可读还是异常                                                                       
                            if (FD_ISSET(fd, &wfds)) {
                                                                                                                                                        /   //
                /* Can write data again   
                 * We better give it a little sleep,                                                                            
                 * for writing large blocks with less times of retry
             */
                _SCHEDULE();
                continue;
                }
                                                                                                if (FD_ISSET(fd, &efds)) {
                                                                                                                                                    /        /
            /* Error occurs */
                                                                                                                                                           
                err = -1;
                                                                                                                                                           
                break;
                                                                                                                                                            }
        }                                                                                                                                                         }                                                                   
        else if (err < 0)
                                                                                                                                                        /        /
        /* Error occurs */
                                                                                                                                                           
            break;
                                                                                                                                               
    }                                                                            }

    if (!nc && err < 0)
        return -1;


    if (!nc)
    /* EAGAIN */
    return -1;

    return nc;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章