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 變量中。所以在判斷是否有錯誤的時候,要處理
這兩種情況。
代碼:
- int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)
- {
- int flags, n, error, code;
- socklen_t len;
- fd_set wset;
- struct timeval tval;
- flags = fcntl(sockfd, F_GETFL, 0);
- fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
- error = 0;
- if ((n == connect(sockfd, saptr, salen)) == 0) {
- goto done;
- } else if (n < 0 && errno != EINPROGRESS){
- return (-1);
- }
- /* Do whatever we want while the connect is taking place */
- FD_ZERO(&wset);
- FD_SET(sockfd, &wset);
- tval.tv_sec = nsec;
- tval.tv_usec = 0;
- if ((n = select(sockfd+1, NULL, &wset,
- NULL, nsec ? &tval : NULL)) == 0) {
- close(sockfd); /* timeout */
- errno = ETIMEDOUT;
- return (-1);
- }
- if (FD_ISSET(sockfd, &wset)) {
- len = sizeof(error);
- code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
- /* 如果發生錯誤,Solaris實現的getsockopt返回-1,
- * 把pending error設置給errno. Berkeley實現的
- * getsockopt返回0, pending error返回給error.
- * 我們需要處理這兩種情況 */
- if (code < 0 || error) {
- close(sockfd);
- if (error)
- errno = error;
- return (-1);
- }
- } else {
- fprintf(stderr, "select error: sockfd not set");
- exit(0);
- }
- done:
- fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
- return (0);
- }
轉自::http://kenby.iteye.com/blog/1183579