1. 程式人生 > >TCP解決connect函式的超時問題

TCP解決connect函式的超時問題

在一個TCP套介面被設定為非阻塞之後呼叫connect,connect會立即返回EINPROGRESS錯誤,表示連線操作正在進行中,但是仍未完成;同時TCP的三路握手操作繼續進行;在這之後,我們可以呼叫select來檢查這個連結是否建立成功;非阻塞connect有三種用途:
1.我們可以在三路握手的同時做一些其它的處理.connect操作要花一個往返時間完成,而且可以是在任何地方,從幾個毫秒的區域網到幾百毫秒或幾秒的廣域網.在這段時間內我們可能有一些其他的處理想要執行;

2.由於我們使用select來等待連線的完成,因此我們可以給select設定一個時間限制,從而縮短connect的超時時間.在大多數實現中,connect的超時時間在75秒到幾分鐘之間.有時候應用程式想要一個更短的超時時間,使用非阻塞connect就是一種方法;

  1. timeval tm;  
  2. fd_set set;  
  3. unsigned long ul = 1;  
  4. ioctlsocket(sock, FIONBIO, &ul); //設定為非阻塞模式
  5. bool ret = false;  
  6. if (connect(...) == -1)  
  7. {  
  8.     tm.tv_set  = TIME_OUT_TIME;  
  9.     tm.tv_uset = 0;  
  10.     FD_ZERO(&set);  
  11.     FD_SET(sock, &set);  
  12.     if (select(sock, NULL, &set, NULL, &tm
    ) > 0)  
  13.     {  
  14.         getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);  
  15.         if (error == 0)  
  16.         {  
  17.             ret = true;  
  18.         }  
  19.         else
  20.         {  
  21.             ret = false;  
  22.         }  
  23.     }  
  24.     else
  25.     {  
  26.         ret = false;  
  27.     }  
  28. }  
  29. else
  30. {  
  31.     ret = true
    ;  
  32. }  
  33. ul = 0;  
  34. ioctlsocket(sock, FIONBIO, &ul); //設定為阻塞模式
  35. if(!ret)   
  36. {  
  37.     close( sockfd );  
  38.     printf(stderr , "Cannot Connect the server!/n");  
  39.     return;  
  40. }  
  41. printf( stderr , "Connected!/n");  

程式碼思路: 

1.建立socket
 2
.將該socket設定為非阻塞模式
 3
.呼叫connect()
 4
.使用select()檢查該socket描述符是否可寫

5.根據select()返回的結果判斷connect()結果
 6
.將socket重設定為阻塞模式

所謂阻塞函式,是指其完成指定的任務之前不允許程式呼叫另一個函式,在Windows下還會阻塞本執行緒訊息的傳送。

所謂非阻塞函式,是指操作啟動之後,如果可以立即得到結果就返回結果,否則返回表示結果需要等待的錯誤資訊,不等待任務完成函式就返回。

首先,非同步函式是非阻塞函式;

其次,獲取遠地資訊的資料庫函式是阻塞函式(因此,WinSock提供了其非同步版本);

下面對具體函式做解釋:

  1. int select(  
  2.   __in          int nfds,//本引數忽略,僅起到相容作用
  3.   __in_out      fd_set* readfds,//指向一組等待可讀性檢查的套介面,可為NULL
  4.   __in_out      fd_set* writefds,//指向一組等待可寫性檢查的套介面,可為NULL
  5.   __in_out      fd_set* exceptfds,//指向一組等待錯誤檢查的套介面,可為NULL
  6.   __in          conststruct timeval* timeout//select()最多等待時間,對阻塞操作則為NULL
  7. );  

 本函式用於確定一個或多個套介面的狀態。對每一個套介面,呼叫者可查詢它的可讀性、可寫性及錯誤狀態資訊。用fd_set結構來表示一組等待檢查的套介面

 readfds引數標識等待可讀性檢查的套介面。如果該套介面正處於監聽listen()狀態,則若有連線請求到達,該套介面便被標識為可讀,這樣一個accept()呼叫保證可以無阻塞完成。對其他套介面而言,可讀性意味著有排隊資料供讀取。或者對於SOCK_STREAM型別套介面來說,相對於該套介面的虛套介面已關閉,於是recv()或recvfrom()操作均能無阻塞完成

writefds引數標識等待可寫性檢查的套介面。如果一個套介面正在connect()連線(非阻塞),可寫性意味著連線順利建立。如果套介面並未處於connect()呼叫中,可寫性意味著send()和sendto()呼叫將無阻塞完成。〔但並未指出這個保證在多長時間內有效,特別是在多執行緒環境中〕。

exceptfds引數標識等待帶外資料存在性或意味錯誤條件檢查的套介面。請注意如果設定了SO_OOBINLINE選項為假FALSE,則只能用這種方法來檢查帶外資料的存在與否。對於SO_STREAM型別套介面,遠端造成的連線中止和KEEPALIVE錯誤都將被作為意味出錯。如果套介面正在進行連線connect()(非阻塞方式),則連線試圖的失敗將會表現在exceptfds引數中。

返回值:
   select()呼叫返回處於就緒狀態並且已經包含在fd_set結構中的描述字總數;如果超時則返回0;否則的話,返回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError()獲取相應錯誤程式碼。