計算機網絡協議第九章,TCP連接的建立與終止

      TCP協議作爲有狀態的服務,本章圍繞這一主題進行詳細講解。本章主要分解連接建立和終止兩個部分闡述TCP的狀態,並且對TCP連接建立和終止的一些常見問題進行分析。


TCP的狀態機

下面我們看一下TCP狀態機的圖例:


一共分爲兩個部分,第一部分是關於連接建立的5個狀態,第二部分是關於連接終止的7個狀態。
連接建立狀態是ESTABLISHED狀態及其以上半部分,連接終止則是其下半部分。
首先初始狀態爲CLOSED狀態,通過三步握手成功後轉換爲ESTABLISHED狀態。最好通過4步鏈接終止最終再次轉換到CLOSED狀態,這就是網絡連接狀態機的閉環。
下面通過連接的建立和拆除分解的方式來理解TCP狀態機。

網絡連接的建立


左側一方爲連接的主動方,通常是一個client角色,右側則是連接的接受方,通常是一個server 角色。
步驟一、主動方通過connect 調用會發送一個SYN的TCP分節,此時socket 狀態從CLOSED狀態轉換爲SYN_SENT狀態。
步驟二、接受方通過listen監聽到SYN分節後,此時socket狀態從CLOSED狀態轉換爲SYN_RECV狀態。並且發送SYN,ACK分節。
步驟三、主動方收到接受方的SYN,ACK分節後socket狀態轉換爲ESTABLISHED, 並且發送ACK分節。
步驟四、接受方收到ACK分節後socket狀態切換爲ESTABLISHED狀態。

連接雙方都是經過一發一收兩個步驟,最終都進入ESTABLISHED狀態。看起來這個TCP連接建立也非常簡單,但是裏面也蘊含很多設計的思想。
在發送SYN分節時,需要初始化seq(Sequence Number簡寫)的值,如果細心的同學通過wireshark等抓包工具查看的時候發現seq=0,但是實際上seq是不爲0的,因爲抓包工具爲了方便使用者分析而採用了相對seq。一般內核協議棧會根據時間的因子做一個隨機的seq值,保證每次連接時儘可能不同。
接受方發送了SYN,ACK分節中的seq也是需要初始化一個值,是一個道理,而ACK Number則是需要在上一個SYN分節的seq上加1。
發送方收到SYN,ACK分節後,ACK Number也是需要加1,道理是同樣的,因爲當TCP的Data爲0時也需要加1處理,如果TCP的Data有10個字節,ACK Number可以表示有多少字節被收到,ACK Number和Seq Number是相輔相成的,ACK Number可以告知發送方多少數據被接收,它被用於重傳機制。Seq Number的保證每次發送時的序號不同,這樣就能夠保證發送報文的順序。

下面看下連接建立後,發送1個字節的數據的相互流程,就比較容易理解Seq Number和Ack Number了


網絡連接的終止


左側我們稱爲連接主動關閉方,右側爲被動關閉方。通過close調用就可以發送FIN分節。
步驟一、主動方發送FIN分節後,socket狀態從ESTABLISHED狀態轉換位FIN_WAIT_1。
步驟二、被動方收到FIN分節後,進入到CLOSE_WAIT狀態並且發送ACK分節確認。
步驟三、被動方調用close函數,發送FIN分節,socket轉換切換爲LAST_ACK狀態
步驟四、主動方收到步驟二發來的ACK分節後進入FIN_WAIT_2狀態。
步驟五、主動方收到步驟三發來的FIN分節後進入TIME_WAIT狀態,並且回覆ACK分節。
步驟六、被動方收到ACK分節後,socket狀態重新回到CLOSED狀態。

連接的關閉流程確實比建立複雜一些,可以看到被動關閉方最終進入到CLOSED狀態,而主動關閉方卻進入到TIME_WAIT狀態。主要看第一張圖,當TIME_WAIT狀態經過2個MSL週期後後最終進入到CLOSED狀態,因此形成狀態機的閉環。

TCP連接建立和終止擴展

連接同時打開

在第一張圖的如下圖所示部分就是同時打開的情況。

當主動方發生SYN分節後進入到SYN-SENT狀態後,正常而言應該是等待SYN,ACK分節,但是等到的是SYN分節,這就以爲則連接的對方同時發生SYN分節,這就叫同時打開,此時應該回復ACK分節並且進入到SYN-RECEIVED狀態,等待ACK分節,而對方的情況也是如此,因此都會進入到SYN-RECEIVED狀態等待ACK,如此就會出現連接過程中四個分節,2個SYN和2個ACK,當每方都收到對方的ACK分節後同時進入到ESTABLISHED狀態。

這裏講一個關於同時打開的小話題,如果用TCP鏈接本機的某一個服務端口的時候,某些情況下會出現“鬼打牆”的現象,就是自己發送的數據又被自己給收到了,出現這個問題的原因就是因爲出現自連接,而自連接就是一直同時打開的現象,其實很好復現這個問題,字節建立一個連接,然後又使用同一個fd連接自己監聽的端口。 當然有時不是故意爲之也會出現,比如你像連接本機的1234端口,而這個12345端口的服務沒有起來,而正好系統把它分給給你創建的fd,當你去連接這個端口的時候就變成自連接了。

連接同時關閉


上節沒有將CLOSING這個狀態,這個狀態只會出現在同時關閉的時候,不少很常見,當一方發送FIN分節進入到FIN_WAIT_1狀態是,正常情況下應該等等ACK分節,可是等待的卻是FIN分節,這就意味着同時關閉,然此時應該進入到CLOSING狀態並且等帶對方的ACK,如果收到ACK則進入到TIME_WAIT狀態。

SYN超時和SYN FLOOD攻擊

SYN超時是如此,當發送SYN一段時間沒有迴應時,會不間斷的發送SYN包以防止丟包,而發送時間間隔會不停的地址,一般直觀感受是connect等待30秒(根據內核而定)左右後會提示連接超時。也就是TCP內核會嘗試多次,如果依然沒有迴應纔會超時。

SYN FLOOD 攻擊是一個比較常見的DOS攻擊手段,它的手段是發送SYN分節報文後,不在響應SYN,ACK分節,讓對方一直無法完成連接的建立,linux內核有一個隊列保持這種連接,並且隊列有一定大小,通過內核參數tcp_syncookies而定,如果內核的隊列被這種SYN報文多次攻擊後,會佔滿內核的隊列導致正常的連接無法等到響應。形成DOS攻擊。

解決這個方法有三個參數可以調配,第一個是:tcp_synack_retries 可以用他來減少重試次數;第二個是:tcp_max_syn_backlog,可以增大SYN連接數;第三個是:tcp_abort_on_overflow 處理不過來乾脆就直接拒絕連接了。一般大公司會有專門保持TCP連接的機器,他們修改內核並有自己保護的策略,能夠扛住百萬級甚至更高的SYN FLOOD攻擊。

FIN_WAIT_1狀態和半關閉

寫個HTTP服務的同學可能會遇到HTTP的QPS效率出現異常而CPU和內存還有很多空間的情況,然後才發現原來80端口有很多socket處於FIN_WAIT_1狀態,出現這個狀態是因爲HTTP服務端關閉了連接,但是客戶端沒有調用close,發送FIN分節,導致服務socket長期處於FIN_WAIT_1狀態導致fd句柄佔用,從而導致服務響應性能下降。這個現象就稱爲半關閉狀態。

那網絡內核就一直不清理FIN_WAIT_1狀態嗎,也不是, 而是要等很長一段時間才能清理,可以將TCP的心跳包活時常設置短一些,這樣能夠加快對FIN_WAIT_1狀態的回收。

TIME_WAIT狀態和MSL

先解釋MSL,MSL的意思爲最大傳輸週期,也就是IP報文在網絡中存活的最大時常,它在Linux中被設置爲30秒,可以認爲MSL=30秒。

什麼是TIME_WAIT狀態,這個狀態的目的是什麼? TIME_WAIT狀態是主動斷開連接方最終進入的狀態,爲什麼要進入到TIME_WAIT狀態呢,因爲TCP設計者認爲如果沒有這個狀態,一個連接斷開後,這個端口如果再次被使用並且向上一次連接目的地址發送報文,那麼可能上一個連接發送的報文在網絡上延時後再次到達目的地址,這樣目的地址會同時收到上一次連接和這一次連接的數據,有可能無法區分而帶來問題。因此TCP設計者認爲如果進入到TIME_WAIT後,必須等到2*MSL時間端口才能被複用,這樣纔會比較安全。
還記得初始化Seq Number吧,上面提到要使用一個隨機值來初始化Seq Number,它也是一種防止這種現象的設計,當然也不一定完全有效,但是大多數時候是有用的。

HTTP服務是短連接的情況,如果Server端主動端開連接就會產生TIME_WAIT狀態,然後TIME_WAIT狀態也是佔用系統資源的,這也是爲什麼大牛設計系統時都是讓客戶端主動端口連接的原因了,當然也不是所有場景都能讓客戶端先端開連接的,系統提供tcp_max_tw_buckets參數來控制TIME_WAIT的最大數量。

當客戶端的場景下,如果重啓進程後會出現上一個連接TIME_WAIT狀態,當時有想複用這個端口,那麼socket提供SO_REUSE_ADDRESS參數來複用TIME_WAIT狀態的端口。

close於shutdown調用

在編寫TCP協議會碰到這樣一個問題,就是發送完數據後關閉連接,對方並沒有將數據全部收全,原因在於調用close函數,以爲你當自己的接受緩存中有數據可讀時,但是沒有全部讀到應用層而直接調用close函數,會發送RST分節,如果發送RST分節,這樣寫緩衝的數據將會被丟棄,因此對方是無法收全你發送的數據的,正確的做法是調用shutdown函數,然後使用recv函數將接受緩衝隊列的數據收全,當對方收到FIN分節後,也好發送ACK分節和FIN分節,如此shutdown調用方式也好收到recv == 0的時間,這時再調用close方法關閉fd。


小結

本章主要講解TCP的狀態機,並且通過講解TCP連接建立和終止的坑來加深對TCP協議的理解,掌握如何避免掉進這些坑中。TCP的因爲有狀態的服務會帶來一定性能問題,在媒體傳輸中不是被首選使用,後續有機會再講下SCTP和T/TCP這兩種協議。

參考

《TCP/IP詳解-協議》卷一     W.Richard Stevens

修訂

初稿                                       2015-3-27               Simon


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