原文連結:http://gcody.blog.ccidnet.com/blog-htm-do-showone-type-blog-itemid-217292-uid-36931.html
[From]http://dev.cbw.com/c/c/200510195601_4292587.shtml
1.首先將標誌位設為Non-blocking模式,準備在非阻塞模式下調用connect函數
2.調用connect,正常情況下,因為TCP三向交握需要一些時間;而非阻塞調用只要不能立即完成就會返回錯誤,所以這裡會返回EINPROGRESS,表示在建立串連但還沒有完成。
3.在讀套介面描述符集(fd_set rset)和寫套介面描述符集(fd_set wset)中將當前套介面置位(用FD_ZERO()、FD_SET()宏),並設定好逾時時間(struct timeval *timeout)
4.調用select( socket, &rset, &wset, NULL, timeout )
返回0表示connect逾時
如果你設定的逾時時間大於75秒就沒有必要這樣做了,因為核心中對connect有逾時限制就是75秒。
[From]http://www.ycgczj.com.cn/34733.html
網
絡編程中socket的分量我想大家都很清楚了,socket也就是套介面,在套介面編程中,提到逾時的概念,我們一下子就能想到3個:發送逾時,接收超
時,以及select逾時(註:
select函數並不是只用於套介面的,但是套介面編程中用的比較多),在connect到目標主機的時候,這個逾時是不由我們來設定的。不過正常情況下
這個逾時都很長,並且connect又是一個阻塞方法,一個主機不能串連,等著connect返回還能忍受,你的程式要是要試圖串連多個主機,恐怕遇到多
個不能串連的主機的時候,會塞得你受不了的。我也廢話少說,先說說我的方法,如果你覺得你已掌握這種方法,你就不用再看下去了,如果你還不瞭解,我願意與
你分享。本文是已在Linux下的程式為例子,不過拿到Windows中方法也是一樣,無非是換幾個函數名字罷了。
Linux中要給connect設定逾時,應該是有兩種方法的。一種是該系統的一些參數,這個方法我不講,因為我講不清楚:P,它也不是編程實現的。另外一種方法就是變相的實現connect的逾時,我要講的就是這個方法,原理上是這樣的:
1.建立socket
2.將該socket設定為非阻塞模式
3.調用connect()
4.使用select()檢查該socket描述符是否可寫(注意,是可寫)
5.根據select()返回的結果判斷connect()結果
6.將socket設定為阻塞模式(如果你的程式不需要用阻塞模式的,這步就省了,不過一般情況下都是用阻塞模式的,這樣也容易管理)
如果你對網路編程很熟悉的話,其實我一說出這個過程你就知道怎麼寫你的程式了,下面給出我寫的一段程式,僅供參考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect逾時時間20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以伺服器位址填充結構serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //設定為非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
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;
ioctl(sockfd, FIONBIO, &ul); //設定為阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面還可以進行發包收包操作
……………
}
以上程式碼片段,僅供參考,也是為初學者提供一些提示,主要用到的幾個函數,select, ioctl, getsockopt都可以找到相關資料,具體用法我這裡就不贅述了,你只需要在linux中輕輕的敲一個man <函數名>就能夠看到它的用法。
此外我需要說明的幾點是,雖然我們用ioctl把套介面設定為非阻塞模式,不過select本身是阻塞的,阻塞的時間就是其逾時的時間由調用select
的時候的最後一個參數timeval類型的變數指標指向的timeval結構變數來決定的,timeval結構由一個表示秒數的和一個表示微秒數
(long類型)的成員組成,一般我們設定了秒數就行了,把微妙數設為0(註:1秒等於100萬微秒)。而select函數另一個值得一提的參數就是上面
我們用到的fd_set類型的變數指標。調用之前,這個變數裡面存了要用select來檢查的描述符,調用之後,針對上面的程式這裡面是可寫的描述符,我
們可以用宏FD_ISSET來檢查某個描述符是否在其中。由於我這裡只有一個套介面描述符,我就沒有使用FD_ISSET宏來檢查調用select之後這
個sockfd是否在set裡面,其實是需要加上這個判斷的。不過我用了getsockopt來檢查,這樣才可以判斷出這個套介面是否是真的串連上了,因
為我們只是變相的用select來檢查它是否串連上了,實際上select檢查的是它是否可寫,而對於可寫,是針對以下三種條件任一條件滿足時都表示可寫
的:
1)套介面發送緩衝區中的可用控制項位元組數大於等於套介面發送緩衝區低潮限度的當前值,且或者i)套介面已串連,或者ii)套介面不要求串連(UDP方式的)
2)串連的寫這一半關閉。
3)有一個套介面錯誤待處理。
這樣,我們就需要用getsockopt函數來擷取套介面目前的一些資訊來判斷是否真的是串連上了,沒有串連上的時候還能給出發生了什麼錯誤,當然我程式中並沒有標出那麼多狀態,只是簡單的表示可串連/不可串連。
下面我來談談對這個程式測試的結果。我針對3種情形做了測試:
1. 目標機器網路正常的情況
可以串連到目標主機,並能成功以阻塞方式進行發包收包作業。
2. 目標機器網路斷開的情況
在等待設定的逾時時間(上面的程式中為20秒)後,顯示目標主機不能串連。
3. 程式運行前斷開目標機器網路,逾時時間內,恢複目標機器的網路
在恢複目標主機網路連接之前,程式一隻等待,恢複目標主機後,程式顯示串連目標主機成功,並能成功以阻塞方式進行發包收包作業。
以上各種情況的測試結果表明,這種設定connect逾時的方法是完全可行的。我自己是把這種設定了逾時的connect封裝到了自己的類庫,用在一套監
控系統中,到目前為止,運行還算正常。這種編程實現的connect逾時比起修改系統參數的那種方法的有點就在於它只用於你的程式之中而不影響系統。