Linux中TCP連接過程狀態簡介

一、Linux服務器上11種網絡連接狀態:


4036295646_eb2b2b957d
      圖:TCP的狀態機

通常情況下,一個正常的TCP連接,都會有三個階段:1、TCP三次握手; 2、數據傳送; 3、TCP四次揮手

注:以下說明最好能結合”圖:TCP的狀態機”來理解。

SYN: (同步序列編號,Synchronize Sequence Numbers)該標誌僅在三次握手建立TCP連接時有效。表示一個新的TCP連接請求。

ACK: (確認編號,Acknowledgement Number)是對TCP請求的確認標誌,同時提示對端系統已經成功接收所有數據。

FIN: (結束標誌,FINish)用來結束一個TCP回話.但對應端口仍處於開放狀態,準備接收後續數據。

1)、LISTEN:首先服務端需要打開一個socket進行監聽,狀態爲LISTEN. /* The socket is listening for incoming connections. 偵聽來自遠方TCP端口的連接請求 */
2)、SYN_SENT:客戶端通過應用程序調用connect進行active open.於是客戶端tcp發送一個SYN以請求建立一個連接.之後狀態置爲SYN_SENT. /*The socket is actively attempting to establish a connection. 在發送連接請求後等待匹配的連接請求 */

3)、SYN_RECV:服務端應發出ACK確認客戶端的SYN,同時自己向客戶端發送一個SYN. 之後狀態置爲SYN_RECV /* A connection request has been received from the network. 在收到和發送一個連接請求後等待對連接請求的確認 */

4)、ESTABLISHED: 代表一個打開的連接,雙方可以進行或已經在數據交互了。/* The socket has an established connection. 代表一個打開的連接,數據可以傳送給用戶 */

5)、FIN_WAIT1:主動關閉(active close)端應用程序調用close,於是其TCP發出FIN請求主動關閉連接,之後進入FIN_WAIT1狀態./* The socket is closed, and the connection is shutting down. 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認 */

6)、CLOSE_WAIT:被動關閉(passive close)端TCP接到FIN後,就發出ACK以迴應FIN請求(它的接收也作爲文件結束符傳遞給上層應用程序),並進入CLOSE_WAIT. /* The remote end has shut down, waiting for the socket to close. 等待從本地用戶發來的連接中斷請求 */

7)、FIN_WAIT2:主動關閉端接到ACK後,就進入了FIN-WAIT-2 ./* Connection is closed, and the socket is waiting for a shutdown from the remote end. 從遠程TCP等待連接中斷請求 */

8)、LAST_ACK:被動關閉端一段時間後,接收到文件結束符的應用程序將調用CLOSE關閉連接。這導致它的TCP也發送一個 FIN,等待對方的ACK.就進入了LAST-ACK . /* The remote end has shut down, and the socket is closed. Waiting for acknowledgement. 等待原來發向遠程TCP的連接中斷請求的確認 */

9)、TIME_WAIT:在主動關閉端接收到FIN後,TCP就發送ACK包,並進入TIME-WAIT狀態。/* The socket is waiting after close to handle packets still in the network.等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認 */

10)、CLOSING: 比較少見./* Both sockets are shut down but we still don’t have all our data sent. 等待遠程TCP對連接中斷的確認 */

11)、CLOSED: 被動關閉端在接受到ACK包後,就進入了closed的狀態。連接結束./* The socket is not being used. 沒有任何連接狀態 */
TIME_WAIT狀態的形成只發生在主動關閉連接的一方。
  主動關閉方在接收到被動關閉方的FIN請求後,發送成功給對方一個ACK後,將自己的狀態由FIN_WAIT2修改爲TIME_WAIT,而必須再等2倍 的MSL(Maximum Segment Lifetime,MSL是一個數據報在internetwork中能存在的時間)時間之後雙方纔能把狀態 都改爲CLOSED以關閉連接。目前RHEL裏保持TIME_WAIT狀態的時間爲60秒。當然上述很多TCP狀態在系統裏都有對應的解釋或設置,可見man tcp

二、關於長連接和短連接:
  通俗點講,短連接就是一次TCP請求得到結果後,連接馬上結束.而長連接並不馬上斷開,而一直保持着,直到長連接TIMEOUT(具體程序都有相關參數說明).長連接可以避免不斷的進行TCP三次握手和四次揮手.
長連接(keepalive)是需要靠雙方不斷的發送探測包來維持的,keepalive期間服務端和客戶端的TCP連接狀態是ESTABLISHED.目前http 1.1版本里默認都是keepalive(1.0版本默認是不keepalive的),ie6/7/8和firefox都默認用的是http 1.1版本了(如何查看當前瀏覽器用的是哪個版本,這裏不再贅述)。Apache,java

  一個應用至於到底是該使用短連接還是長連接,應該視具體情況而定。一般的應用應該使用長連接。

tcp 四次揮手

  TCP協議有一個優雅的關閉(graceful close)機制,以保證應用程序在關閉連接時不必擔心正在傳輸的數據會丟失。如第4.5節的壓縮示例程序所示,這個機制還設計爲允許兩個方向的數據傳輸相互獨立地終止。關閉機制的工作流程是:應用程序通過調用連接套接字的close()方法或shutdownOutput()方法表明數據已經發送完畢。此刻,底層的TCP實現首先將留存在SendQ隊列中的數據傳輸出去(還要依賴於另一端RecvQ隊列的剩餘空間),然後向另一端發送一個關閉TCP連接的握手消息。該關閉握手消息可以看作是流終止標誌:它告訴接收端TCP不會再有新的數據傳入RecvQ隊列了。(注意,關閉握手消息本身並沒有傳遞給接收端應用程序,而是通過read()方法返回-1來指示其在字節流中的位置。)正在關閉的TCP將等待其關閉握手消息的確認信息,該確認信息表明在連接上傳輸的所有數據已經安全地傳輸到了RecvQ中。只要收到了確認消息,該連接就變成"半關閉(Half closed)"狀態。直到連接的另一個方向上收到了對稱的握手消息後,連接才完全關閉--也就是說,連接的兩端都表明它們再沒有數據要發送了。

  TCP連接的關閉事件序列可能以兩種方式發生:一種方式是先由一個應用程序調用close()方法(或shutdownOutput()方法),並在另一端調用close()方法之前完成其關閉握手消息;另一種方式是兩端同時調用close()方法,它們的關閉握手消息在網絡上交叉傳輸。圖6.10展示了以第一種方式關閉連接時,底層實現中的事件序列。關閉握手消息已經發送,套接字數據結構的狀態也已經設置爲"Closing"(專業術語稱爲"FIN_WAIT_1"),然後close()調用返回。完成這些工作後,將禁止在該Socket上的任何讀寫操作(會拋出異常)。當收到關閉握手確認消息後,套接字數據結構的狀態則改變爲"半關閉"(專業術語稱爲"FIN_WAIT_2"),這種狀態將一直持續,直到接收到另一端的關閉握手消息

  關閉TCP連接的最後微妙之處在於對Time-Wait狀態的需要。TCP規範要求在終止連接時,兩端的關閉握手都完成後,至少要有一個套接字在Time-Wait狀態保持一段時間。這個要求的提出是由於消息在網絡中傳輸時可能延遲。如果在連接兩端都完成了關閉握手後,它們都移除了其底層數據結構,而此時在同樣一對套接字地址之間又立即建立了新的連接,那麼前一個連接在網絡上傳輸時延遲的消息就可能在新連接建立後到達。由於其包含了相同的源地址和目的地址,舊消息就會被錯誤地認爲是屬於新連接的,其包含的數據就可能被錯誤地分配到應用程序中。

  雖然這種情形可能很少發生,TCP還是使用了包括Time-Wait狀態在內的多種機制對其進行防範。Time-Wait狀態用於保證每個TCP連接都在一段平靜時間內結束,這期間不會有數據發送。平靜時間的長度應該等於分組報文在網絡上存留的最長時間的兩倍。因此,當一個連接完全結束(即套接字數據結構離開Time-Wait狀態並被刪除),併爲同樣一對地址上的新連接清理道路後,就不會再有舊實例發送的消息還存留在網絡中。實際上,平靜時間的長度要依賴於具體實現,因爲沒有機制能真正限制分組報文在網絡上能夠延遲的時間。通常使用的時間範圍是4分鐘減到30秒,或更短。

  Time-Wait狀態最重要的作用是,只要底層套接字數據結構還存在,就不允許在相同的本地端口上關聯其他套接字。尤其是試圖使用該端口創建新的Socket實例時,將拋出IOException異常。


TCP三次握手/四次揮手詳解

1、建立連接協議(三次握手)
(1)客戶端發送一個帶SYN標誌的TCP報文到服務器。這是三次握手過程中的報文1。
(2) 服務器端迴應客戶端的,這是三次握手中的第2個報文,這個報文同時帶ACK標誌和SYN標誌。因此它表示對剛纔客戶端SYN報文的迴應;同時又標誌SYN給客戶端,詢問客戶端是否準備好進行數據通訊。
(3) 客戶必須再次迴應服務段一個ACK報文,這是報文段3。

2、連接終止協議(四次揮手)
   由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
 (1) TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送(報文段4)。
 (2) 服務器收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。
 (3) 服務器關閉客戶端的連接,發送一個FIN給客戶端(報文段6)。
 (4) 客戶段發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)。

CLOSED: 這個沒什麼好說的了,表示初始狀態。

LISTEN: 這個也是非常容易理解的一個狀態,表示服務器端的某個SOCKET處於監聽狀態,可以接受連接了。

SYN_RCVD: 這個狀態表示接受到了SYN報文,在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一箇中間狀態,很短暫,基本上用netstat你是很難看到這種狀態的,除非你特意寫了一個客戶端測試程序,故意將三次TCP握手過程中最後一個ACK報文不予發送。因此這種狀態時,當收到客戶端的ACK報文後,它會進入到ESTABLISHED狀態。

SYN_SENT: 這個狀態與SYN_RCVD遙想呼應,當客戶端SOCKET執行CONNECT連接時,它首先發送SYN報文,因此也隨即它會進入到了SYN_SENT狀態,並等待服務端的發送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已發送SYN報文。

ESTABLISHED:這個容易理解了,表示連接已經建立了。

FIN_WAIT_1: 這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。

FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連接,也即有一方要求close連接,但另外還告訴對方,我暫時還有點數據需要傳送給你,稍後再關閉連接。

TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。

CLOSING: 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你發送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方幾乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。

CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給自己,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有數據發送給對方,如果沒有的話,那麼你也就可以close這個SOCKET,發送FIN報文給對方,也即關閉連接。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連接。

LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。

最後有2個問題的回答,我自己分析後的結論(不一定保證100%正確):

1、 爲什麼建立連接協議是三次握手,而關閉連接卻是四次握手呢?
這是因爲服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裏來發送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這裏的ACK報文和FIN報文多數情況下都是分開發送的。

2、 爲什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?
這是因爲:雖然雙方都同意關閉連接了,而且握手的4個報文也都協調和發送完畢,按理可以直接回到CLOSED狀態(就好比從SYN_SEND狀態到ESTABLISH狀態那樣);但是因爲我們必須要假想網絡是不可靠的,你無法保證你最後發送的ACK報文會一定被對方收到,因此對方處於LAST_ACK狀態下的SOCKET可能會因爲超時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT狀態的作用就是用來重發可能丟失的ACK報文。


發佈了30 篇原創文章 · 獲贊 30 · 訪問量 74萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章