簡介:
所謂阻塞模式,是指其完成指定的操作之前阻塞當前的程序或執行緒,直到操作有結果返回.
在我們直接呼叫socket操作函式時,如果不進行特意宣告的話,它們都是工作在阻塞模式的,
如 connect, send, recv等.
更多關於阻塞/非阻塞,同步/非同步的講解可以參見我總結的相關專題文章:
簡單分類的話,可以將超時處理分成兩類:
連線(connect)超時;
傳送(send), 接收(recv)超時;
下面對這兩類超時一一做示例講解
一、連線(connect)超時
基本實現流程如下:
1.建立socket;
2.將該socket設定為非阻塞(non-blocking)模式;
3.呼叫connect();
正常情況下,因為tcp三次握手需要一些時間;
而非阻塞呼叫只要不能立即完成就會返回錯誤,
所以這裡會返回einprogress,表示在建立連線但還沒有完成。
4. 在讀套介面描述符集(fd_set readset)和寫套介面描述符集(fd_set writeset)中
將當前套介面置位(用fd_zero()、fd_set()巨集);
並設定好超時時間(struct timeval *timeout);
如果你設定的超時時間大於75秒就沒有必要這樣做了,因為核心中對connect有超時限制就是75秒。
5.使用select()檢查該socket描述符是否可寫(注意,是可寫);
6.根據select()返回的結果判斷connect()結果
返回0表示connect超時;
7.將socket設定為阻塞模式;
如果你的程式不需要用阻塞模式的,這步就省了,
不過一般情況下都是用阻塞模式的,這樣也容易管理;
下面是示例**的實現:
/** \brief
* tcp client
*/#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define true 1
#define false 0
#define servport 8080
#define maxdatasize 100
#define timeout_time 10
int main(int argc, char *argv);*/
struct sockaddr_in server_addr;
/* */
fd_set readset, writeset;
struct timeval timeout;
unsigned long ul = 1;
int error = -1, len = sizeof(int);
int btimeoutflag = false;
int ret;
if (argc < 3)
*snd_buf = '\0';
strcat(snd_buf, argv[2]);
if ((sockfd = socket(af_inet, sock_stream, 0)) == -1)
server_addr.sin_family = af_inet;
server_addr.sin_port = htons(servport);
inet_pton(af_inet, argv[1], &server_addr.sin_addr);
memset(&(server_addr.sin_zero), 0, 8);
/*setting socket to non-blocking mode */
ioctl(sockfd, fionbio, &ul);
/* create the connection by socket
* means that connect "sockfd" to "server_addr"
*/if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
else
goto end;
}else if ( ret == -1) // 返回-1, 有錯誤發生,錯誤原因存在於errno
else // 成功,返回描述詞狀態已改變的個數
}else
ul = 0;
ioctl(sockfd, fionbio, &ul); //重新將socket設定成阻塞模式
/* 同步阻塞模式,未設定超時 */
if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1)
printf("send:%s\n", snd_buf);
if ((recvbytes = recv(sockfd, rcv_buf, maxdatasize, 0)) == -1)
rcv_buf[recvbytes] = '\0';
printf("recv:%s\n", rcv_buf);
end:
close(sockfd);
return 0;
}以上**,僅供參考,也是為初學者提供一些提示;
主要用到的幾個函式,select, ioctl, getsockopt都可以找到相關資料;
需要再說明的是:
1. 雖然用ioctl把套介面設定為非阻塞模式,但select本身是阻塞的,
阻塞的時間就是其超時的時間,
由呼叫select 的時候的最後乙個引數timeval型別的變數指標指向的timeval結構變數來決定的,
timeval結構由乙個表示秒數的和乙個表示微秒數(long型別)的成員組成,
一般我們設定了秒數就行了,把微妙數設為0(注:1秒等於100萬微秒)。
2. select函式中另乙個值得一提的引數就是上面我們用到的fd_set型別的變數指標。
呼叫之前,這個變數裡面存了要用select來檢查的描述符,
呼叫之後,針對上面的程式這裡面是可寫的描述符,我們可以用巨集fd_isset來檢查某個描述符是否在其中。
由於這裡只有乙個套介面描述符,就沒有使用fd_isset巨集來檢查呼叫select之後這個sockfd是否在set裡面,
其實是需要加上這個判斷的。
不過這裡用了getsockopt來檢查,這樣才可以判斷出這個套介面是否是真的連線上了,
因為我們只是變相的用select來檢查它是否連線上了,
實際上select檢查的是它是否可寫,而對於可寫,是針對以下三種條件任一條件滿足時都表示可寫的:
1) 套介面傳送緩衝區中的可用控制項位元組數大於等於套介面傳送緩衝區低潮限度的當前值,
且或者i) 套介面已連線,或者
ii)套介面不要求連線(udp方式的)
2) 連線的寫這一半關閉。
3) 有乙個套介面錯誤待處理。
這樣,我們就需要用getsockopt函式來獲取套介面目前的一些資訊來判斷是否真的是連線上了,
沒有連線上的時候還能給出發生了什麼錯誤,
當然我程式中並沒有標出那麼多狀態,只是簡單的表示可連線/不可連線。
下面談談對這個程式測試的結果。這裡針對3種情形做了測試:
1. 目標機器網路正常的情況
可以連線到目標主機,並能成功以阻塞方式進行發包收包作業。
2.目標機器網路斷開的情況
在等待設定的超時時間(上面的程式中為10秒)後,顯示目標主機不能連線。
3.程式執行前斷開目標機器網路,超時時間內,恢復目標機器的網路
在恢復目標主機網路連線之前,程式乙隻等待;
恢復目標主機後,程式顯示連線目標主機成功,並能成功以阻塞方式進行發包收包作業。
以上各種情況的測試結果表明,這種設定connect超時的方法是完全可行的。
九五,飛龍在天,利見大人。
【白話】九五,龍飛上了高空,利於出現德高勢隆的大人物。
《象》曰:「飛龍在天」,大人造也。
【白話】《象辭》說:「龍飛上了高空」,象徵德高勢隆的大人物一定會有所作為。
from:
socket 超時設定
在send recv 過程中有時由於網路狀況等原因,收發不能預期進行,而設定收發超時控制 這樣做在linux環境下是不會產生效果的,須如下定義 struct timeval timeout 設定傳送超時 setsockopt socket,sol socket,so sndtimeo,char ti...
socket超時設定
在使用socket程式設計時,肯定會遇到設定超時的問題。有些人可能會認為socket類的setsotimeout sotimeout 方法就是設定超時。其實不然,socket設定超時分為兩種,上面提到為讀寫超時。第一 建立連線的超時設定,如下 socket.connect socketaddress...
socket程式設計中的超時設定示例詳解之二
接前文 二 操作 send,recv 超時 對它們有兩種方式來設定超時,1.將send,recv設定成非阻塞模式,然後用select來設定超時機制,就如上面的connect方式一樣。2.使用setsockopt 函式來設定相應的超時機制。因為前一種方式在connect中重點說明,本節來主要說明第二種...