《TCP/IP詳解》之一:連接建立、斷開


  《TCP/IP詳解·卷一》看了三遍才算整明白個大概,一直想做個總結。

  最初對TCP的印象很簡單:丟包重傳、流數據。丟包重傳很好理解,“流數據”是什么鬼?

  知乎上看到個極好的解釋:把TCP看作用管子往對端灌水,水是數據,它們之間沒有邊界,且先發先到;UDP是往對端滾小球,它們之間有明確邊界,且可能每個小球速度不同,先滾的不一定先到,得自己處理亂序。

  編碼上也可看出,TCP的send回調帶有dwNumberOfBytesTransferred參數,描述本次網絡IO發送了多少字節數據,而不是給它多少發多少。很可能讓發的500字節只發了300,剩200留着呢,業務層得自行處理。接收方也有個常規的粘包操作,把連成一片的網絡buffer解析為單個消息包,再分發給各業務邏輯。

  那就試着復述下這個管子灌水的過程吧。

  首先是管道的建立、拆除。即非常有名的TCP三次握手。

  終止要經過四次握手。

  為什么會多一次呢?從圖中可以看出,建立時報文段2包含了SYN、ACK,而終止時FIN與ACK是分兩條發的,why?

  TCP連接是雙全工的,兩端都能收發,向對方交付FIN僅僅只是告知對端自己不再發送數據(仍須能收,即TCP的半關閉),而對端何時終止發送,是其業務邏輯決定的。終止時報文段2、報文段3的觸發,源自兩個不同層次的東西,無法合並成一條(即使有ack延時確認)。

  剩下的問題是,建立/終止的這些報文,任意一條都會丟失。其中最麻煩的是終止握手的最后一條ACK,它的重傳依賴對端FIN的超時,所以主動發起關閉的一方不能在收到最后一個FIN時,即刻關閉連接。

  這就是非常著名的TIME_WAIT狀態。主動關閉方要等2×MSL(報文段最大生成時間)后方可真正關閉連接,留有足夠的時間應對最后ACK的丟失(對端重傳FIN)。

  然后這個TIME_WAIT在使用上引起了些麻煩——此狀態的連接,不能再接收數據(報文段直接拋棄),也不能重用(舊連接的在路上的數據可能會被當成新連接的)。

  在服務器端就要注意,避免短時間內大量關閉socket,產生的眾多TIME_WAIT連接可能會耗盡系統資源,致使新連接無法建立。

  以為這就完了嗎?

  Quiet Time。

  平靜時間:TCP在重啟后的MSL秒內不能建立任務連接。

  如若沒有Quiet Time,TIME_WAIT的2×MSL出現故障怎么辦?比如服務器中途宕機很快重啟,並按流程立即使用了之前同樣的一對socket。假設故障發生時那對舊socket恰好有報文段在網絡上,那重啟后的新連接就完全無法區分此條報文段了,會被當作正常數據接收。

  

  在分析斷開為何要四次時介紹了TCP半關閉,相應的也有TCP半打開。

  比如客戶端忽然斷電停機,由於沒來得及跟服務器有交互,所以服務器是不知道對端已關閉的。只要不在此連接上傳送數據,就永遠不會知曉另一方已然完蛋。

  TCP四大定時器(重傳、堅持、保活、2MSL)之一的保活定時器要解決的問題。

  一條連接在給定時間內沒任何交互,服務器會向客戶端發送探查報文,若超時后仍未收到回應,或收到RST復位(客戶端已重啟),即終止該連接。

 

  當然還有“同時打開/同時關閉”的問題。與上述常規打開/關閉相比,只是狀態遷移有所不同,對照示意圖一看即明。

  這里再介紹下連接建立過程中的“呼入請求隊列”。

  三次握手成功是個及時操作,系統不可能緊盯着它等處理。那服務器繁忙時,有連接到了怎么辦?很簡單,排隊,等有空了挨個處理。

  “呼入請求隊列”便是這個。需要注意的:客戶端connect成功,僅僅只表示被放入了svr的呼入請求隊列(此時client仍能發數據,收的數據被放在tcp緩存了),還未交接給應用層。svr端Accept時才會將隊列中的連接取出,進而處理。

  PS:如果不取或取慢了,client會因tcp發送緩沖滿而一直send失敗。所以怎么調Accept也是要考慮設計的

  PPS:“呼入請求隊列”有長度限制(積壓值 backlog),超過后再來的連接請求就一直失敗了。

 

  業務層上,關閉比建立更難些,難點在於:發送數據的完整性(極可能你的用戶buffer/tcp緩沖中都殘留有正常的邏輯數據),以及關閉的及時性。

  針對TCP層的發送緩沖,有“優雅關閉、強制關閉”兩種。優雅關閉是指,如果發送緩存中還有數據未發出則其發出去,並且收到所有數據的ACK之后,發送FIN包,開始關閉過程;強制關閉是指,如果緩存中還有數據,則丟棄這些數據,然后發送RST包,直接重置TCP連接。

  針對用戶層的發送緩沖,正統做法是:將closeflag置true,業務層的buffer不再接數據了,繼續調tcpSend直至buffer數據被發完,隨即“優雅關閉”——TCP緩沖發完后FIN,客戶端收到后回FIN(四次握手關閉連接),svr收到FIN,進而closesocket。

  客戶端異常就收不到回的FIN了,所以得要個“優雅關閉列表”,數秒后還不關就強關。

  還有土豪做法,先將連接設置成無效的,業務層不收也不發了,等足夠長時間(3分鍾)后,強關~~

  

  

  

  


免責聲明!

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



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