socket 的讀寫超時時間超過十分鐘,數據包會重傳16次。參考:http://my.oschina.net/lowkey2046/blog/694229服務器
咱們能夠經過設置 socket 選項 SO_SNDTIMEO 和 SO_RCVTIMEO 來減小讀寫 socket 的等待時間。網絡
在源碼 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)); /* * 省略 */ }
服務器程序在樹莓派上,IP 地址爲 192.168.1.24。測試
測試客戶端爲 PC 機,IP 地址爲 192.168.1.21。.net
啓動客戶端程序後,輸入一行 "hello world" 測試網絡是否連通。code
./client 192.168.1.24 hello world hello world
客戶端收到了服務端的數據,說明鏈接正常。server
在客戶端輸入 "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; }
能夠看到,在 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》