在非阻塞下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;
}

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