C Socket編程之Connect超時 (轉)


網絡編程中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超時比起修改系統參數的那種方法的有點就在於它只用於你的程序之中而不影響系統。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM