7、TCP C/S:socket 讀寫超時設置

socket 的讀寫超時時間超過十分鐘,數據包會重傳16次。參考:http://my.oschina.net/lowkey2046/blog/694229服務器

咱們能夠經過設置 socket 選項 SO_SNDTIMEO 和 SO_RCVTIMEO 來減小讀寫 socket 的等待時間。網絡

1. 程序源碼

在源碼 http://my.oschina.net/lowkey2046/blog/693852 基礎上進行修改。socket

在建立 socket 後,經過 setsockopt 函數修改讀寫超時時間便可。測試時只修改客戶端 socket 的超時。tcp

client函數

int main(int argc, char **argv)
{
    struct timeval      tv;

    /*
     * 省略
     */

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    /* 設置套接字讀寫超時 */
    tv.tv_sec = 3;
    tv.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    /* 
     * 省略
     */
}

2. 測試過程

1) 啓動服務端程序

服務器程序在樹莓派上,IP 地址爲 192.168.1.24。測試

2) 啓動客戶端

測試客戶端爲 PC 機,IP 地址爲 192.168.1.21。.net

啓動客戶端程序後,輸入一行 "hello world" 測試網絡是否連通。code

./client 192.168.1.24
 hello world
 hello world

客戶端收到了服務端的數據,說明鏈接正常。server

3) 斷開樹莓派網絡,客戶端再次發送數據

在客戶端輸入 "hello world":blog

hello world
 str_cli: server terminated prematurely

能夠看到,大 3 秒後 read 函數出錯返回,程序終止。若是經過 perror 查看出錯緣由:Resource temporarily unavailable;而未設置超時的時候 perror 輸出:Connection timed out。出錯緣由是不同的。

read 出錯位置以下:

if ((recvn = read(sockfd, recvbuf, sizeof(recvbuf)-1)) < 0) {
	fprintf(stderr, "str_cli: server terminated prematurely");
	return;
} else if(recvn == 0) {
	fprintf(stderr, "str_cli: read EOF\n");
	return;
}

3. wireshark

輸入圖片說明

能夠看到,在 15.46 秒時,客戶端程序發送了一個數據包,以後一直重傳該數據包,嘗試獲得服務端的響應。

在 18.46 秒時,客戶端程序發送了一個 FIN。此時的客戶端程序由於讀超時而終止。

以後,以前的數據包和 FIN 被做爲一個 TCP 重傳。重傳時間超過了一分鐘。(後面應該沒了,我等了很久都沒有數據包)

須要注意的是,客戶端程序終止後,系統仍舊在重傳數據包。這意味着,設置 socket 的讀寫超時選項 SO_RCVTIMEO、SO_SNDTIMEO 只會影響 read、write等讀寫 socket 函數,系統並不會在超時後中止重傳,也不會標記該 socket 爲錯誤。這也意味着,程序將數據寫入系統 socket 緩衝區後,就不用擔憂程序終止時系統會將 socket 緩衝區數據直接丟棄(對方鏈接正常且緩衝區數據還未發送成功的狀況下)。

若是在 read 超時後繼續向該 sockt 寫入數據呢?在系統 socket 緩衝區未滿的狀況下 write 不會阻塞,能夠繼續寫入數據。(下次在寫個代碼證明)

另外 read 出錯返回,說明了 write 返回時成功的,但此時服務器並無接收到數據。這說明 write 只負責將數據成功的寫到系統 socket 緩衝區,至於對方有沒有收到數據是無論的。

Q: 爲何是一分鐘?以前 read 不是超過10分鐘嗎,重傳16次嗎?

緣由在這:

netstat -nat | grep 9000
tcp        0      8 192.168.1.21:45602      192.168.1.24:9000       FIN_WAIT1

客戶端終止後,發送 FIN,此時鏈接進入 'FIN_WAIT1' 階段。

參考資料

《UNP》

相關文章
相關標籤/搜索