TCP/IP詳解--連線狀態變遷圖CLOSE WAIT
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
終止一個連線要經過4次握手。這由TCP的半關閉(half-close)造成的。既然一個TCP連線是全雙工(即資料在兩個方向上能同時傳遞,可理解為兩個方向相反的獨立通道),因此每個方向必須單獨地進行關閉。這原則就是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向連線。當一端收到一個FIN例如:TCP客戶端傳送一個FIN,用來關閉從客戶到伺服器的資料傳送。
半關閉對伺服器究竟有什麼影響呢?先看看下面的TCP狀態轉化圖
tcp狀態裝換圖
首先對上面這個圖示進行解釋:
CLOSED:表示初始狀態。對服務端和C客戶端雙方都一樣。
LISTEN:表示監聽狀態。服務端呼叫了listen函式,可以開始accept連線了。
SYN_SENT:表示客戶端已經發送了SYN報文。當客戶端呼叫connect函式發起連線時,首先發SYN給服務端,然後自己進入SYN_SENT狀態,並等待服務端傳送ACK+SYN。
SYN_RCVD:表示服務端收到客戶端傳送SYN報文。服務端收到這個報文後,進入SYN_RCVD狀態,然後傳送ACK+SYN給客戶端。
ESTABLISHED:表示連線已經建立成功了。服務端傳送完ACK+SYN後進入該狀態,客戶端收到ACK後也進入該狀態。
FIN_WAIT_1:表示主動關閉連線。無論哪方呼叫close函式傳送FIN報文都會進入這個這個狀態。
FIN_WAIT_2:表示被動關閉方同意關閉連線。主動關閉連線方收到被動關閉方返回的ACK後,會進入該狀態。
TIME_WAIT:表示收到對方的FIN報文併發送了ACK報文,就等2MSL後即可回到CLOSED狀態了。如果FIN_WAIT_1狀態下,收到對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
CLOSING:表示雙方同時關閉連線。如果雙方几乎同時呼叫close函式,那麼會出現雙方同時傳送FIN報文的情況,此時就會出現CLOSING狀態,表示雙方都在關閉連線。
CLOSE_WAIT:表示被動關閉方等待關閉。當收到對方呼叫close函式傳送的FIN報文時,迴應對方ACK報文,此時進入CLOSE_WAIT狀態。
LAST_ACK:表示被動關閉方傳送FIN報文後,等待對方的ACK報文狀態,當收到ACK後進入CLOSED狀態。
三次握手和四次握手所對應的上述狀態:
客戶端主動關閉時,發出FIN包,收到伺服器的ACK,客戶端停留在FIN_WAIT2狀態。而服務端收到FIN,發出ACK後,停留在COLSE_WAIT狀態。
這個CLOSE_WAIT狀態非常討厭,它持續的時間非常長,伺服器端如果積攢大量的COLSE_WAIT狀態的socket,有可能將伺服器資源耗盡,進而無法提供服務。
那麼,伺服器上是怎麼產生大量的失去控制的COLSE_WAIT狀態的socket呢?我們來追蹤一下。
一個很淺顯的原因是,伺服器沒有繼續發FIN包給客戶端。
伺服器為什麼不發FIN,可能是業務實現上的需要,現在不是傳送FIN的時機,因為伺服器還有資料要發往客戶端,傳送完了自然就要通過系統呼叫發FIN了,這個場景並不是上面我們提到的持續的COLSE_WAIT狀態,這個在受控範圍之內。
那麼究竟是什麼原因呢,咱們引入兩個系統呼叫close(sockfd)和shutdown(sockfd,how)接著往下分析。
在這兒,需要明確的一個概念---- 一個程序開啟一個socket,然後此程序再派生子程序的時候,此socket的sockfd會被繼承。socket是系統級的物件,現在的結果是,此socket被兩個程序開啟,此socket的引用計數會變成2。
繼續說上述兩個系統呼叫對socket的關閉情況。
呼叫close(sockfd)時,核心檢查此fd對應的socket上的引用計數。如果引用計數大於1,那麼將這個引用計數減1,然後返回。如果引用計數等於1,那麼核心會真正通過發FIN來關閉TCP連線。
呼叫shutdown(sockfd,SHUT_RDWR)時,核心不會檢查此fd對應的socket上的引用計數,直接通過發FIN來關閉TCP連線。
如何避免這樣的情況發生?
子程序的關閉處理應該是這樣的:
shutdown(sockfd,SHUT_RDWR);
close(sockfd);
這樣處理,伺服器的FIN會被髮出,socket進入LAST_ACK狀態,等待最後的ACK到來,就能進入初始狀態CLOSED。
補充一下shutdown()的函式說明
linux系統下使用shutdown系統呼叫來控制socket的關閉方式
int shutdown(intsockfd,int how);
引數how允許為shutdown操作選擇以下幾種方式:
SHUT_RD:關閉連線的讀端。也就是該套接字不再接受資料,任何當前在套接字接受緩衝區的資料將被丟棄。程序將不能對該套接字發出任何讀操作。對TCP套接字該呼叫之後接受到的任何資料將被確認然後被丟棄。
SHUT_WR:關閉連線的寫端。
SHUT_RDWR:相當於呼叫shutdown兩次:首先是以SHUT_RD,然後以SHUT_WR
注意:
在多程序中如果一個程序中shutdown(sfd, SHUT_RDWR)後其它的程序將無法進行通訊.如果一個程序close(sfd)將不會影響到其它程序.