【零散隨筆】深入理解TIME_WAIT及相關問題

小引:在我之前的幾篇TCP與UDP服務器的實現中沒有注意到TIME_WAIT這個細節,引來了一個問題。就是我在調試的時候,想要重啓一下服務器,直接ctrl+c然後重選上一個命令。我發現總是會給我報出一個bind error: Address already in use的錯誤,爲什麼會綁定出錯呢,我根據提示地址已經被使用,輸入了netstat -anp tcp查看到我的端口還沒有被關閉,可我明明已經ctrl+c了呀。


TIME_WAIT是指在四次揮手的第四次時服務器向客戶端發送FIN請求,客戶端收到FIN請求後進入TIME_WAIT時間,然後客戶端再向服務器發送FIN_ACK,服務器收到FIN_ACK之後關閉連接。

爲什麼會有TIME_WAIT?TIME_WAIT對鏈接結束過程有什麼影響?

1.本質:將等待的時間交給客戶端,避免降低服務器的性能。

我們可以假設沒有TIME_WAIT,客戶端收到FIN併發送FIN_ACK之後立即關閉。如果該FIN_ACK丟失,服務器會認爲,自己發的FIN請求沒有被收到,於是會再次發送FIN請求,但是此時客戶端已經關閉,所以不會再回復。服務器就會進入反覆FIN請求的狀態,多次沒收到回覆之後再斷開該客戶端的鏈接,大量消耗服務器資源,是極其不合理的。

如果有TIME_WAIT,再發生上述的FIN_ACK丟失,此時客戶端套接字還沒有關閉,再次收到FIN請求後,能夠快速重發FIN_ACK,這樣就極大的避免了服務器等待已關閉鏈接的情況。

如果TIME_WAIT期間,客戶端沒有再次收到FIN請求,此時對於客戶端來說,沒有消息就是好消息,客戶端會認爲服務器已經收到了FIN_ACK並關閉了套接字。然後TIME_WAIT時間結束後,客戶端再去銷燬自己的套接字。

因此TIME_WAIT的時間被設置爲2MSL(Max Segment Life)的時間。

2.額外:TIME_WAIT期間,還可以讓通信信道中鏈接時的正常數據儘量不要丟失。

數據在沒有TCP,只有IP層的時候,有可能出現亂序的情況。也就是說,如果兩者發送的正常數據不幸被阻塞在鏈接途徑中的某臺路由器上,但是發送關閉鏈接請求時,路由器發現之前有數據阻塞,零時調整了算法,導致後發的關閉鏈接請求先於正常數據到達,如果沒有TIME_WAIT時間,遲到的正常數據就無法被接收,因而造成數據缺失。

TIME_WAIT帶來的問題?

首先要知道兩點服務器主動斷開連接時,會進入TIME_WAIT狀態

TIMEWAIT期間客戶端並沒有CLOSE,所以我們認爲此時鏈接還沒有斷開

bind error: Address already in use

服務器一旦掛掉,鏈接還沒有立即斷開,服務器和客戶端的一對套接字還在被佔用,端口號也還在被佔用,所以此時立刻重啓服務器,就會出現綁定錯誤,因爲端口號還在被佔用。

解決方法:

1.(簡單粗暴)

換個端口號把服務器開起來

2.opt

在操作系統層面認爲,如果你的鏈接進入TIMEWAIT狀態,服務也就接近尾聲,所以即便是TIMEWAIT狀態,也允許端口號,被再次綁定。也就有了如下接口來實現該屬性。

使用setsockopt()設置socket描述符的選項SO_REUSEADDR爲1,表示允許創建端口號相同,但ip地址不同的套接字。

在ser端代碼的socket()和bind()之間插入如下代碼

int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章