非阻塞connect



http://blog.163.com/li_xiang1102/blog/static/60714076201110298170975/ 


步驟1: 設置非阻塞,啓動連接

實現非阻塞 connect ,首先把 sockfd 設置成非阻塞的。這樣調用

connect 可以立刻返回,根據返回值和 errno 處理三種情況:

(1) 如果返回 0,表示 connect 成功。

(2) 如果返回值小於 0, errno 爲 EINPROGRESS,  表示連接

      建立已經啓動但是尚未完成。這是期望的結果,不是真正的錯誤。

(3) 如果返回值小於0,errno 不是 EINPROGRESS,則連接出錯了。

 

步驟2:判斷可讀和可寫

然後把 sockfd 加入 select 的讀寫監聽集合,通過 select 判斷 sockfd

是否可寫,處理三種情況:

(1) 如果連接建立好了,對方沒有數據到達,那麼 sockfd 是可寫的

(2) 如果在 select 之前,連接就建立好了,而且對方的數據已到達,

      那麼 sockfd 是可讀和可寫的。

(3) 如果連接發生錯誤,sockfd 也是可讀和可寫的。

判斷 connect 是否成功,就得區別 (2) 和 (3),這兩種情況下 sockfd 都是

可讀和可寫的,區分的方法是,調用 getsockopt 檢查是否出錯。

 

步驟3:使用 getsockopt 函數檢查錯誤

getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)

在 sockfd 都是可讀和可寫的情況下,我們使用 getsockopt 來檢查連接

是否出錯。但這裏有一個可移植性的問題。

如果發生錯誤,getsockopt 源自 Berkeley 的實現將在變量 error 中

返回錯誤,getsockopt 本身返回0;然而 Solaris 卻讓 getsockopt 返回 -1,

並把錯誤保存在 errno 變量中。所以在判斷是否有錯誤的時候,要處理

這兩種情況。

 

代碼:

 

C代碼 複製代碼 收藏代碼
  1. int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)  
  2. {  
  3.     int flags, n, error, code;  
  4.     socklen_t len;  
  5.     fd_set wset;  
  6.     struct timeval tval;  
  7.   
  8.     flags = fcntl(sockfd, F_GETFL, 0);  
  9.     fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);  
  10.   
  11.     error = 0;  
  12.     if ((n == connect(sockfd, saptr, salen)) == 0) {  
  13.         goto done;  
  14.     } else if (n < 0 && errno != EINPROGRESS){  
  15.         return (-1);  
  16.     }  
  17.   
  18.     /* Do whatever we want while the connect is taking place */  
  19.   
  20.     FD_ZERO(&wset);  
  21.     FD_SET(sockfd, &wset);  
  22.     tval.tv_sec = nsec;  
  23.     tval.tv_usec = 0;  
  24.   
  25.     if ((n = select(sockfd+1, NULL, &wset,   
  26.                     NULL, nsec ? &tval : NULL)) == 0) {  
  27.         close(sockfd);  /* timeout */  
  28.         errno = ETIMEDOUT;  
  29.         return (-1);  
  30.     }  
  31.   
  32.     if (FD_ISSET(sockfd, &wset)) {  
  33.         len = sizeof(error);  
  34.         code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);  
  35.         /* 如果發生錯誤,Solaris實現的getsockopt返回-1, 
  36.          * 把pending error設置給errno. Berkeley實現的 
  37.          * getsockopt返回0, pending error返回給error.  
  38.          * 我們需要處理這兩種情況 */  
  39.         if (code < 0 || error) {  
  40.             close(sockfd);  
  41.             if (error)   
  42.                 errno = error;  
  43.             return (-1);  
  44.         }  
  45.     } else {  
  46.         fprintf(stderr, "select error: sockfd not set");  
  47.         exit(0);  
  48.     }  
  49.   
  50. done:  
  51.     fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */  
  52.     return (0);  
  53. }  


  轉自::http://kenby.iteye.com/blog/1183579 


發佈了73 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章