1. 程式人生 > >TCP狀態查看以及故障的排查

TCP狀態查看以及故障的排查

splay acc sta isp 跳轉 lose 能夠 程序 accept

TCP三次握手過程中的服務端和客戶端的各種狀態:

TCP四次握手釋放過程中的主動關閉端和被動關閉端的各種狀態:

下圖的兩端可以是服務端也可以是客戶端。

四次握手釋放過程中,主動關閉這一端會處於TIME_WAIT,被動關閉這一端會馬上處於CLOSE狀態,處於TIME_WAIT的一端要等幾十秒後會才會到CLOSE狀態釋放端口。

註意:

listen函數只是讓套接字加上監聽能力,不是使套接字開始進入監聽狀態。

讓套接字由closed狀態進入listen監聽狀態的是accept函數。

ESTABLISHED 數據可傳輸狀態

能夠在服務端或客戶端捕獲看到的狀態

1、三次握手:

服務端的LISTEN

客戶端的ESTABLISHED

服務端的ESTABLISHED

(1)只開啟服務端multi_thread_server4,可以看到處於LISTEN監聽狀態

切換到root後,netstat -anp|grep 端口號

技術分享圖片

(1)服務端和客戶端連接成功

服務端處於ESTABLISHED 數據可傳輸狀態

技術分享圖片

客戶端處於ESTABLISHED 數據可傳輸狀態

技術分享圖片

2、四次握手釋放:

主動關閉這一端的FIN_WAIT2

被動關閉這一端的CLOSE_WAIT

主動關閉這一端的TIME_WAIT

(1)連接成功後關閉客戶端,服務端沒關閉

主動關閉的一端處於FIN_WAIT_2狀態,被動關閉的一端(沒關閉的一端)處於CLOSE_WAIT的狀態。關閉服務端後這個CLOSE_WAIT狀態要幾十秒才會消失。

技術分享圖片

客戶端:

技術分享圖片

服務端:

技術分享圖片

(2)連接成功後關閉服務端,客戶端沒關閉

服務端:

技術分享圖片

客戶端:

技術分享圖片

(3)四次握手釋放過程中,先關閉客戶端再關閉服務端

四次握手釋放過程中,主動關閉這一端發送完ACK包後會處於TIME_WAIT,被動關閉這一端會馬上處於CLOSE狀態(這個狀態捕獲不到),處於TIME_WAIT的一端要等幾十秒後會才會到CLOSE狀態釋放端口。

客戶端:

技術分享圖片

(4)先關閉服務端再關閉客戶端

經常會遇到先關閉服務端再關閉客戶端後,再啟動服務端會出現下圖情況,要等幾十秒才能啟動,這是因為服務端處於TIME_WAIT,端口還沒釋放(例程裏的服務端端口是8888)。

技術分享圖片

服務端:

技術分享圖片

為什麽要這個TIME_WAIT呢?

為了保證四次握手釋放過程中安全地關閉端口。

為了保證四次握手釋放過程中應答ACK包成功發給服務端。服務端主動關閉後,會發生FIN數據包給客戶端,客戶端會返回一個應答ACK包。如果這個應答包服務沒有接受到則會認為FIN數據包發送失敗,會再次發送發生FIN數據包。所以這裏需要一個一個時間 2MSL來處於TIME_WAIT狀態。

處於TIME_WAIT的時間為 2MSL,MSL表示最大片生存時間,這個時間大概是30s,2MSL就是60s。這個MSL就等於TTL(一個TCP數據包在路由間跳轉的生存時間),設置為 2MSL是因為在需要重發FIN數據包的時候(ACK應答包丟失的情況),發送ACK數據包和重發FIN數據包的TTL時間是 2MSL,客戶端為了保證重發的FIN數據包能夠到達。如果沒有這個TIME_WAIT,當服務端的有其他進程馬上使用這個端口(例程裏的服務端端口是8888)的時候,則重發的FIN數據包會發生給使用了該端口的進程,這時這個進程就不明白這個FIN包是為了什麽,可能會導致不可預知的bug。也有可能某些在未過TTL時間的FIN數據包還在路由間跳轉,只要服務端的TIME_WAIT持續時間大於MSL就能夠保證不被幹擾。

如果是客戶端處於TIME_WAIT(客戶端是主動關閉這一端)則客戶端可以使用其他端口,不會出現端口被占用的警告。

客戶端:

連接成功後,關閉客戶端後,再開一個客戶端連接,可能看到被關閉的客戶端還在處於處於TIME_WAIT,而且和再開的客戶端端口不同。

技術分享圖片

上面說的FIN和ACK數據包是指下圖紅框的數據包

以上是順利正常握手和釋放的情況,下面是握手和釋放過程中不順利的情況。

三次握手不順利的情況

服務端程序裏有listen_fd和client_fd兩種套接字,listen_fd用來監聽來自客戶端的連接請求,每次連接完一個客戶端就回到accept這裏繼續阻塞等待監聽,client_fd用來接收來自客戶端的數據或者發送數據給客戶端。

3、RST包

一開始服務端和客戶端都處於CLOSED狀態,服務端啟動後listen_fd套接字處於監聽狀態LISTEN,監聽來自客戶端的SYN包後發送SYN+ACK包給客戶端。

順利的話客戶端接收到後返回ACK包給服務端端後創建client_fd套接字和子進程或者子線程,子進程或者子線程裏client_fd處於數據可傳輸狀態ESTABLISHED,然後父進程或者主線程的listen_fd回到LISTEN狀態。

但當不順利時,在客戶端發送SYN後客戶端關閉,客戶端處於CLOSED狀態,服務端發送SYN+ACK包給處於CLOSED狀態客戶端時,客戶端會返回一個RST包給服務端,listen_fd由SYN_RCVD狀態變為LISTEN狀態。

技術分享圖片

4、

本來客戶端A是調用connect函數發送SYN包去主動連接服務器的,是處於SYN_SEND狀態的,但是這時有另外一個客戶端B發送SYN包給客戶端A,想要去連接客戶端A,則客戶端A會轉變為SYN_RCVD狀態。

5、

客戶端發送SYN後,網絡不通暢,客戶端接收不到SYN+ACK包,超時後回到CLOSED狀態

四次握手釋放不順利的情況

假設服務端和客戶端的client_fd套接字處於ESTABLISHED狀態。

技術分享圖片

6、順利四次握手釋放的過程

主動關閉一端

主動關閉這一端調用close函數,發送FIN包給被動關閉這一端後處於FIN_WAIT_1,被動關閉這一端返回ACK包後,主動關閉這一端處於FIN_WAIT_2。當被動關閉這一端發FIN並且主動關閉這一端返回ACK給被動關閉這一端後,主動關閉這一端處於TIME_WAIT並持續2MSL後回到CLOSE狀態。

被動關閉一端

被動關閉這一端接收到FIN包返回ACK包給主動關閉這一端後處於CLOSE_WAIT,等待被動關閉這一端調用close函數發送FIN包給主動關閉這一端後,被動關閉這一端處於LAST_ACK,之後被動關閉這一端會收到ACK包就處於CLOSE狀態。

7、當兩端都主動關閉

主動關閉這一端A端調用close函數,發送FIN包給被動關閉這一端B端後處於FIN_WAIT_1,本來被動關閉這一端B端是要返回ACK包的,但此時被動關閉這一端B也主動關閉了並發送FIN包給主動關閉這一端A端,B端就也處於FIN_WAIT_1並且B端返回ACK包給A端,A端就處於CLOSING並返回ACK包給B端,B端也處於CLOSING狀態。接下來A端和B端會從CLOSING轉為TIME_WAIT,持續2MSL後都轉為CLOSE狀態。

技術分享圖片

8、FIN+ACK包

主動關閉這一端A端調用close函數,發送FIN包給被動關閉這一端B端後處於FIN_WAIT_1,本來被動關閉這一端接下來是要處於CLOSE_WAIT並返回ACK包,等到到被動關閉這一端調用了close函數時會再發送FIN包後處於LAST_ACK。但也有一種情況就是被動關閉這一端在接受到FIN包這一瞬間剛好調用了close函數,此時被動關閉這一端就跳過CLOSE_WAIT馬上處於LAST_ACK,並發送了FIN+ACK包。主動關閉這一端本來還有一個FIN_WAIT_2,現在接收到FIN+ACK包後就跳到TIME_WAIT並發送ACK包給被動關閉這一端,主動關閉這一端持續2MSL後轉為CLOSE狀態,被動關閉這一端收到最後的ACK後也轉為CLOSE狀態。

技術分享圖片

TCP狀態查看以及故障的排查