SELECT裝置逾時用法小結

來源:互聯網
上載者:User

SELECT裝置逾時用法小結

目前各平台通用的設定socket connect逾時的辦法是通過select(),具體方法如下:

1.建立socket;

2.將該socket設定為非阻塞模式;

3.調用connect();

4.使用select()檢查該socket描述符是否可寫;

5.根據select()返回的結果判斷connect()結果;

6.將socket設回阻塞模式。

 

select,就是用來監視某個或某些控制代碼的狀態變化的,執行I/O多路轉換。

select函數原型如下:

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

ndfs:select監視的檔案控制代碼數,視進程中開啟的檔案數而定,一般設為要監視各檔案中的最大檔案號加一。 
readfds:select監視的可讀檔案控制代碼集合。 
writefds: select監視的可寫檔案控制代碼集合。 
exceptfds:select監視的異常檔案控制代碼集合。 
timeout:本次select()的逾時結束時間

當readfds或writefds中映象的檔案可讀或可寫或逾時,本次select()就結束返回。程式員利用一組系統提供的宏在select()結束時便可判斷哪一檔案可讀或可寫。對Socket編程特別有用的就是readfds。幾隻相關的宏解釋如下: 
FD_ZERO(fd_set *fdset):清空fdset與所有檔案控制代碼的聯絡。 
FD_SET(int fd, fd_set *fdset):建立檔案控制代碼fd與fdset的聯絡。 
FD_CLR(int fd, fd_set *fdset):清除檔案控制代碼fd與fdset的聯絡。 
FD_ISSET(int fd, fdset *fdset):檢查fdset聯絡的檔案控制代碼fd是否可讀寫,>0表示可讀寫。

 

函 數的最後一個參數timeout顯然是一個逾時時間值,其類型是struct timeval *,即一個struct timeval結構的變數的指標,所以我們在程式裡要申明一個struct timeval tv;然後把變數tv的地址&tv傳遞給select函數。struct timeval結構如下:

struct timeval

{            

long    tv_sec;         /* seconds */            

long    tv_usec;        /* microseconds */        

};
 

第2、 3、4三個參數是一樣的類型: fd_set *,即我們在程式裡要申明幾個fd_set類型的變數,比如rdfds, wtfds, exfds,然後把這個變數的地址&rdfds, &wtfds, &exfds 傳遞給select函數。

      這三個參數都是一個控制代碼的集合,第一個rdfds是用來儲存這樣的控制代碼的:當控制代碼的狀態變成可讀的時系統就會告訴select函數返回,同理第二個wtfds是指有控制代碼狀態變成可寫的時系統就會告訴select函數返回,同理第三個參數exfds是特殊情況,即控制代碼上有特殊情況發生時系統會告訴select函數返回。特殊情況比如對方通過一個socket控制代碼發來了緊急資料。如果我們程式裡只想檢測某個socket是否有資料可讀,我們可以這樣:

fd_set rdfds; /* 先申明一個 fd_set 集合來儲存我們要檢測的 socket控制代碼 */

struct timeval tv; /* 申明一個時間變數來儲存時間 */

int ret; /* 儲存傳回值 */

FD_ZERO(&rdfds); /* 用select函數之前先把集合清零 */

FD_SET(socket, &rdfds); /* 把要檢測的控制代碼socket加入到集合裡 */

 

tv.tv_sec = 1;

tv.tv_usec = 500; /* 設定select等待的最大時間為1秒加500微秒 */

 

ret = select(socket + 1, &rdfds, NULL, NULL, &tv); /* 檢測我們上面設定到集合rdfds裡的控制代碼是否有可讀資訊 */

if(ret < 0)

    perror("select");/* 這說明select函數出錯 */

else if(ret == 0)

   printf("逾時/n"); /* 說明在我們設定的時間值1秒加500毫秒的時間內,socket的狀態沒有發生變化 */

else

{ /* 說明等待時間還未到1秒加500毫秒,socket的狀態發生了變化 */   

    printf("ret=%d/n", ret); /* ret這個傳回值記錄了發生狀態變化的控制代碼的數目,由於我們只監視了socket這一個控制代碼,所以這裡一定ret=1,如果同時有多個控制代碼發生變化返回的就是控制代碼的總和了 */

    /* 這裡我們就應該從socket這個控制代碼裡讀取資料了,因為select函數已經告訴我們這個控制代碼裡有資料可讀 */  

if(FD_ISSET(socket, &rdfds)) { /* 先判斷一下socket這外被監視的控制代碼是否真的變成可讀的了 */       

   /* 讀取socket控制代碼裡的資料 */       

    recv(...);   

}

}
 

注意select函數的第一個參數,是所有加入集合的控制代碼值的最大那個值還要加1。比如我們建立了3個控制代碼:

 

int sa, sb, sc;

sa = socket(...); /* 分別建立3個控制代碼並串連到伺服器上 */

connect(sa,...);

sb = socket(...);

connect(sb,...);

sc = socket(...);

connect(sc,...);

FD_SET(sa, &rdfds);/* 分別把3個控制代碼加入讀監視集合裡去 */

FD_SET(sb, &rdfds);

FD_SET(sc, &rdfds);
 

在使用select函數之前,一定要找到3個控制代碼中的最大值是哪個,我們一般定義一個變數來儲存最大值,取得最大socket值如下:

int maxfd = 0;

if(sa > maxfd)

     maxfd = sa;

if(sb > maxfd)

    maxfd = sb;

if(sc > maxfd)

    maxfd = sc;
 

然後調用select函數:

ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /* 注意是最大值還要加1 */
 

同樣的道理,如果我們要檢測使用者是否按了鍵盤進行輸入,我們就應該把標準輸入0這個控制代碼放到select裡來檢測,如下:

FD_ZERO(&rdfds);

FD_SET(0, &rdfds);

tv.tv_sec = 1;

tv.tv_usec = 0;

ret = select(1, &rdfds, NULL, NULL, &tv); /* 注意是最大值還要加1 */

if(ret < 0)

     perror("select");/* 出錯 */

else if(ret == 0)

     printf("逾時/n"); /* 在我們設定的時間tv內,使用者沒有按鍵盤 */

else { /* 使用者有按鍵盤,要讀取使用者的輸入 */  

scanf("%s", buf); }

 

 

 

 

 

LINUX設定連線逾時方法:

在阻塞通訊端的一般情況下,connect ()直到用戶端對SYN訊息的ACK訊息到達之前才會返回。使connect()調用具有逾時機制的一個方法是讓通訊端成為非阻塞的通訊端體,然後用select()來等待它完成。
s = socket(AF_INET, SOCK_STREAM, 0);
//下面擷取通訊端的標誌
if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
    //錯誤處理
}

//下面設定通訊端為非阻塞
if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
    //錯誤處理
}

if ((retcode = connect(s, (struct sockaddr*)&peer, sizeof(peer)) && 
    errno != EINPROGRESS) {
   //因為通訊端設為NONBLOCK,通常情況下,串連在connect()返回
   //之前是不會建立的,因此它會返回EINPROGRESS錯誤,如果返回
   //任何其他錯誤,則要進行錯誤處理
}

if (0 == retcode) {  //如果connect()返回0則串連已建立
    //下面恢複通訊端阻塞狀態
    if (fcntl(s, F_SETFL, flags) < 0) {
        //錯誤處理
    }

    //下面是串連成功後要執行的代碼
    
    exit(0)
}

(要裝置send/recv逾時只需從此處開始修改相應值,前面不用)
FD_ZERO(&rdevents);
FD_SET(s, &rdevents);  //把先前的通訊端加到讀集合裡面
wrevents = rdevents;   //寫集合
exevents = rdevents;   //異常集合

tv.tv_sec = 5;  //設定時間為5秒
tv_tv_usec = 0;

retcode = select(s+1, &rdevents, &wrevents, &exevents, &tv);
if (retcode < 0) {  //select返回錯誤???
    //錯誤處理
}
else if (0 == retcode) {  //select 逾時???
    //逾時處理
}
esle {
    //通訊端已經準備好
    if (!FD_ISSET(s, &rdevents) && !FD_ISSET(s, &wrevents)) {
        //connect()失敗,進行錯處理
    }

    if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
        //getsockopt()失敗,進行錯處理
    }

    if (err != 0) {
        //connect()失敗,進行錯處理
    }
(send/recv逾時到此為止,返回send()/recv()函數)

    //到這裡說明connect()正確返回
    //下面恢複通訊端阻塞狀態
    if (fcntl(s, F_SETFL, flags) < 0) {
        //錯誤處理
    }

    //下面是串連成功後要執行的代碼
    
    exit(0)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.