TCP解決connect函式的超時問題
1.我們可以在三路握手的同時做一些其它的處理.connect操作要花一個往返時間完成,而且可以是在任何地方,從幾個毫秒的區域網到幾百毫秒或幾秒的廣域網.在這段時間內我們可能有一些其他的處理想要執行;
2.由於我們使用select來等待連線的完成,因此我們可以給select設定一個時間限制,從而縮短connect的超時時間.在大多數實現中,connect的超時時間在75秒到幾分鐘之間.有時候應用程式想要一個更短的超時時間,使用非阻塞connect就是一種方法;
- timeval tm;
- fd_set set;
- unsigned long ul = 1;
- ioctlsocket(sock, FIONBIO, &ul); //設定為非阻塞模式
- bool ret = false;
- if (connect(...) == -1)
- {
- tm.tv_set = TIME_OUT_TIME;
- tm.tv_uset = 0;
- FD_ZERO(&set);
- FD_SET(sock, &set);
- if (select(sock, NULL, &set, NULL, &tm
- {
- getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
- if (error == 0)
- {
- ret = true;
- }
- else
- {
- ret = false;
- }
- }
- else
- {
- ret = false;
- }
- }
- else
- {
- ret = true
- }
- ul = 0;
- ioctlsocket(sock, FIONBIO, &ul); //設定為阻塞模式
- if(!ret)
- {
- close( sockfd );
- printf(stderr , "Cannot Connect the server!/n");
- return;
- }
- printf( stderr , "Connected!/n");
程式碼思路:
1.建立socket
2.將該socket設定為非阻塞模式
3.呼叫connect()
4.使用select()檢查該socket描述符是否可寫
5.根據select()返回的結果判斷connect()結果
6.將socket重設定為阻塞模式
所謂阻塞函式,是指其完成指定的任務之前不允許程式呼叫另一個函式,在Windows下還會阻塞本執行緒訊息的傳送。
所謂非阻塞函式,是指操作啟動之後,如果可以立即得到結果就返回結果,否則返回表示結果需要等待的錯誤資訊,不等待任務完成函式就返回。
首先,非同步函式是非阻塞函式;
其次,獲取遠地資訊的資料庫函式是阻塞函式(因此,WinSock提供了其非同步版本);
下面對具體函式做解釋:
- int select(
- __in int nfds,//本引數忽略,僅起到相容作用
- __in_out fd_set* readfds,//指向一組等待可讀性檢查的套介面,可為NULL
- __in_out fd_set* writefds,//指向一組等待可寫性檢查的套介面,可為NULL
- __in_out fd_set* exceptfds,//指向一組等待錯誤檢查的套介面,可為NULL
- __in conststruct timeval* timeout//select()最多等待時間,對阻塞操作則為NULL
- );
本函式用於確定一個或多個套介面的狀態。對每一個套介面,呼叫者可查詢它的可讀性、可寫性及錯誤狀態資訊。用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()獲取相應錯誤程式碼。