1. 程式人生 > >簡單說說TCP(3) --- 斷開連線四次握手

簡單說說TCP(3) --- 斷開連線四次握手

四次握手流程

先來一張TCP斷開連線流程圖:(圖片來自於網路)
TCP斷開連線四次握手流程

上圖中,A是主動關閉方,B是被動關閉方,四次握手可以描述為:

  • 第一次握手:A告訴B,“我要關閉連線了”。
  • 第二次握手:B回覆A,“我知道你要關閉了,但是請等一下,我還有資料沒有傳完,你等我訊息”。
  • 第三次握手:B告訴A,“我的資料發完了,你可以關閉連線了”。
  • 第四次握手:A回覆B,“好的,你先關吧,我2MSL時間後再關”。

出現大量CLOSE_WAIT的原因

被動關閉方收到主動關閉方發來的FIN,則會迴應ACK,並進入CLOSE_WAIT狀態。但如果被動關閉方不執行close(),就不能由CLOSE_WAIT遷移到LAST_ACK。在該狀態下,recv/read會返回0。

舉例來說,當主動關閉方呼叫closesocket的時候,被動關閉方的應用程式正在呼叫recv,這時候有可能應用程式沒有收到主動關閉方發來的FIN包,而是由TCP代回了一個ACK,所以就陷入CLOSE_WAIT出不來了。

TCP的KeepLive功能,可以讓作業系統替我們自動清理掉CLOSE_WAIT的連線。但是KeepLive在Windows作業系統下預設是7200秒,需要調整一下:

開啟登錄檔(執行 -> 輸入regedit -> 回車),在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters裡修改:

KeepAliveTime              REG_DWORD  300000
KeepAliveInterval          REG_DWORD  1000
TcpMaxDataRetransmissions  REG_DWORD  5

為什麼要有TIME_WAIT

  • 讓4次握手關閉流程更加可靠。
    若最後一個ACK丟失,被動關閉方會重新發一個FIN。在TIME_WAIT狀態內,主動關閉方收到重發的FIN後,會重發ACK。

    而倘若沒有2MSL的TIME_WAIT狀態,當主動關閉方收到重發的FIN後,會回覆一個RST,被動關閉方在收到RST後,會將其解釋成一個錯誤:connect reset by peer。

  • 等待老的重複分節在網路中消逝,防止lost duplicate對後續新建的incarnation connection的傳輸造成破壞。

    • lost duplicate:在實際的網路中非常常見,經常是由於路由器產生故障,路徑無法收斂,導致一個數據包在路由器A、B、C之間做類似死迴圈的跳轉。IP頭部有個TTL,限制了一個包在網路中的最大跳數。因此這個包有兩種命運,

      • TTL變為0,在網路中消失;
      • TTL在變為0之前路由器路徑收斂,它憑藉剩餘的TTL跳數終於到達目的地。但非常可惜的是TCP通過超時重傳機制在早些時候傳送了一個跟它一模一樣的包,並且先於它到達了目的地,因此它註定被TCP協議棧拋棄;
    • incarnation connection:跟上次的socket pair一模一樣的新連線。倘若沒有2MSL時間的TIME_WAIT狀態來等待lost duplicate失效,那麼就會出現如下情況,

      一個incarnation connection收到的seq=1000,這時來了一個lost duplicate為seq=1000,len=1000,則tcp認為這個lost duplicate合法,並存放入了receive buffer,導致傳輸出現錯誤。

TIME_WAIT狀態為什麼持續2MSL

MSL(Maximum Segment Lifetime)是指報文最大生存時間。RFC 793規範MSL為2min。然後,實際TCP實現中的常用值是30s、1min或2min。

之所以是2MSL而不是1MSL,是因為這裡面還包括了最後一個ACK在路上的時間。

2MSL在Windows上預設是4min。

TIME_WAIT帶來的問題

RFC要求socket pair在處於TIME_WAIT時,不能再起一個incarnation connection。但絕大部分TCP實現,強加了更為嚴格的限制,“在2MSL等待期間,socket中使用的本地埠在預設情況下不能再被使用”。

這個限制對於client來說是無所謂的,但是對於server,就嚴重了。一旦server端主動關閉了某個client的連線,導致server監聽的埠在2MSL時間內無法接受新的連線,這顯然是不行的。

  • 方案一
    保證由client主動發起關閉。

  • 方案二
    server主動關閉的時候使用RST的方式,不進入 TIME_WAIT狀態。
    具體做法是設定socket的SO_LINGER選項。

  • 方案三
    給server的socket設定SO_REUSEADDR選項,這樣的話就算server埠處於TIME_WAIT狀態,在這個埠上依舊可以將服務啟動。

    當然,“非相同socket pair”這個限制依然存在,即在2MSL時間內仍然拒絕來自與之前斷掉的client相同ip和port的連線,錯誤資訊為address already in use。

    程式碼如下:

int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

不過在BSD-derived實現和Windows Server 2003中,只要SYN的seq比上一次關閉時的最大seq還要大,那麼TIME_WAIT狀態一樣接受這個SYN。

  • 方案四
    Linux實現了一個TIME_WAIT狀態快速回收的機制,即無需等待2MSL這麼久的時間,而是等待一個Retrans時間即釋放,也就是等待一個重傳時間(一般超級短,以至於你都來不及能在netstat -ant中看到TIME_WAIT狀態)隨即釋放。
    釋放了之後,一個連線的tuple元素資訊就都沒有了,而此時,新建立的TCP卻面臨著危險:

    • 可能被之前遲到的FIN包給終止
    • 可能被之前連線劫持

    於是需要有一定的手段避免這些危險。什麼手段呢?雖然曾經連線的tuple資訊沒有了,但是在IP層還可以儲存一個peer資訊,注意這個資訊不單單是用於TCP這個四層協議的,路由邏輯也會使用它,其欄位包括但不限於:

    • 對端ip地址
    • peer最後一次被TCP觸控到的時間戳

    在快速釋放掉TIME_WAIT連線之後,peer依然保留著。丟失的僅僅是埠資訊。不過有了peer的IP地址資訊以及TCP最後一次觸控它的時間戳就足夠了,TCP規範給出一個優化,即一個新的連線除了同時觸犯了以下幾點,其它的均可以快速接入,即使它本應該處在TIME_WAIT狀態:

    • 來自同一臺機器的TCP連線攜帶時間戳
    • 之前同一臺peer機器(僅僅識別IP地址,因為連線被快速釋放了,沒了埠資訊)的某個TCP資料在MSL秒之內到過本機
    • 新連線的時間戳小於peer機器上次TCP到來時的時間戳,且差值大於重放視窗戳

    看樣子只有以上的3點的同時滿足才能拒絕掉一個新連線,要比TIME_WAIT機制設定的障礙導致的連線拒絕機率小很多,但是要看到,上述的快速釋放機制沒有埠資訊!這就把機率擴大了65535倍。然而,如果對於單獨的機器而言,這不算什麼,因為單臺機器的時間戳不可能倒流的,出現上述的3點均滿足時,一定是老的重複資料包又回來了。
    但是,一旦涉及到NAT裝置,就悲催了,因為NAT裝置將資料包的源IP地址都改成了一個地址(或者少量的IP地址),但是卻基本上不修改TCP包的時間戳。

    TIME_WAIT快速回收在Linux上通過net.ipv4.tcp_tw_recycle啟用,由於其根據時間戳來判定,所以必須開啟TCP時間戳才有效。

    建議:如果前端部署了三/四層NAT裝置,儘量關閉快速回收,以免發生NAT背後真實機器由於時間戳混亂導致的SYN拒絕問題。

Windows下調整TIME_WAIT時間

1、開啟登錄檔(執行 -> 輸入regedit -> 回車)
2、在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters裡新增一項:

TcpTimedWaitDelay  REG_DWORD  0x0000001e(30)

Linux下調整核心引數

編輯檔案:

vim /etc/sysctl.conf

加入以下內容:

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

最後執行:

/sbin/sysctl -p

使引數生效。

  • net.ipv4.tcp_syncookies = 1
    開啟SYN cookies,當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊。預設為0,表示關閉。

  • net.ipv4.tcp_tw_reuse = 1
    開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉。

  • net.ipv4.tcp_tw_recycle = 1
    開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉。

  • net.ipv4.tcp_fin_timeout = 30
    系統預設的 TIMEOUT 時間。

Linux下kill程序時對socket的關閉處理

Linux照顧到了一種特殊情況,即殺死程序的情況,在系統kill程序的時候,會直接呼叫連線的close函式單方面關閉一個方向的連線,然後並不會等待對端關閉另一個方向的連線程序即退出。現在的問題是,TCP規範和UNIX程序的檔案描述符規範直接衝突!程序關閉了,套接字就要關閉,但是TCP是全雙工的,你不能保證對端也在同一個時刻同意並且實施關閉動作,既然連線不能關閉,作為檔案描述符,程序就不會關閉得徹底!所以,Linux使用了一種“子狀態”的機制,即在程序退出的時候,單方面傳送FIN,然後不等後續的關閉序列即將連線拷貝到一個佔用資源更少的TW套接字,狀態直接轉入TIMW_WAIT,此時記錄一個子狀態FIN_WAIT_2,接下來的套接字就和原來的屬於程序描述符的連線沒有關係了。等到新的連線到來的時候,直接匹配到這個主狀態為TW,子狀態為FIN_WAIT_2的TW連線上,它負責處理FIN,FIN ACK等資料。

統計所有TCP連線的各種狀態的數量

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

相關推薦

簡單說說TCP(3) --- 斷開連線握手

四次握手流程 先來一張TCP斷開連線流程圖:(圖片來自於網路) 上圖中,A是主動關閉方,B是被動關閉方,四次握手可以描述為: 第一次握手:A告訴B,“我要關閉連線了”。 第二次握手:B回覆A,“我知道你要關閉了,但是請等一下,我還有資料沒有傳

TCP建立連線握手和釋放連線握手

TCP的報文結構如下下所示:序列號seq:佔4個位元組,用來標記資料段的順序,TCP把連線中傳送的所有資料位元組都編上一個序號,第一個位元組的編號由本地隨機產生;給位元組編上序號後,就給每一個報文段指派

Wireshark抓包示範:TCP握手建立連線握手斷開連線

以下內容來自網路資源整合,僅供自己記錄,日後檢視方便。 首先介紹Wireshark抓包工具,它長這樣: 下面我們要設定過濾規則: 按如下設定,主要設定: 1、需要監控的網絡卡; 2、過濾規則(圖示是“HTTP TCP port(80)”,即只監控TCP連線):

TCP握手斷開連線

建立連線非常重要,它是資料正確傳輸的前提;斷開連線同樣重要,它讓計算機釋放不再使用的資源。如果連線不能正常斷開,不僅會造成資料傳輸錯誤,還會導致套接字不能關閉,持續佔用資源,如果併發量高,伺服器壓力堪憂。建立連線需要三次握手,斷開連線需要四次握手,可以形象的比喻為下面的對話

TCP/IP協議的三握手揮手(建立連線斷開連線

1、TCP/IP協議概述 TCP/IP協議(TransmissionControl Protocol/Internet Protocol)叫做傳輸控制/網際協議,又叫網路通訊協議,這個協議是Internet國際網際網路絡的基礎。TCP/IP是網路中使用的基本的通訊協議。雖然

TCP建立連接的三握手TCP連接斷開揮手

bubuko 信息 發送數據 數據 可靠的 註意 過程 接收 告訴 1. TCP建立連接的3次握手 2. TCP斷開連接的四次揮手 【註意】中斷連接端可以是Client端,也可以是Server端。 圖3

TCP鏈接的三握手斷開

浪費 seq 建立連接時 tab 背景條 客戶端 設計 received 上帝 一直總覺得三次握手和四次斷開,之前老師講的有問題,經過自己再次琢磨,發現是的,老師講的沒毛病,這次也把自己的理解總結一下,讓對這個知識模糊的小夥伴再換種思路去理解 首先看一下TCP三次握手發生了

TCP連線握手 揮手

前言: TCP協議是面向連線、安全可靠、基於位元組流的傳輸層協議,在進行http協議訪問時就用到了tcp連線。在建立TCP連線時需要經歷三次握手,斷開連線時需要經歷四次揮手。在此進行記錄。 內容:   TCP三次握手    第一次握手:由客戶端發起,客戶端生成一個SYN,以及一個

TCP/IP詳解--TCP握手建立連線握手終止連線

1.TCP連線的建立 (1)首先是伺服器初始化的過程,從CLOSED(關閉)狀態開始通過順序呼叫SOCKET、BIND、LISTEN和ACCEPT原語建立Socket套接字,進入LISTEN(監聽)狀態,等待客戶端的TCP傳輸連線請求。      (2)客戶端最開始也是從CLOSED狀態開始呼叫SOCKET

TCP/IP TIME_WAIT狀態原理(握手關閉連線原理)

TIME_WAIT狀態原理 ---------------------------- 通訊雙方建立TCP連線後,主動關閉連線的一方就會進入TIME_WAIT狀態。 客戶端主動關閉連線時,會發送最後一個ack後,然後會進入TIME_WAIT狀態,再停留2個MSL時間(後有MS

簡單說明TCP的狀態和三握手握手

在TCP層,有個FLAGS欄位,這個欄位有以下幾個標識:SYN, FIN, ACK, PSH, RST, URG. 其中,對於我們日常的分析有用的就是前面的五個欄位。  它們的含義是: SYN表示建立連線, FIN表示關閉連線, ACK表示響應, PSH表示有 DATA資料傳輸, RST表示連線重置。  其

TCP連線握手揮手

1、三次握手 (1)三次握手的詳述 首先Client端傳送連線請求報文,Server段接受連線後回覆ACK報文,併為這次連線分配資源。Client端接收到ACK報文後也向Server段發生ACK報文,並分配資源,這樣TCP連線就建立了。    最初兩端的TCP程

為什麼tcp建立連線是三握手而不是兩握手或者握手?(筆試面試常考)

        先說說tcp三次握手,  不細說了, 也就是syn,  ack/syn,  ack.         為什麼不能是兩次呢?         先假設是兩次吧。我們知道, tcp的連線過程中有一個超時重傳演算法(karn演算法是比較典型的), 如果client發

TCP/IP的三握手連線握手關閉

(1)第一次握手:建立連線時,客戶端A傳送SYN包(SYN=j)到伺服器B,並進入SYN_SEND狀態,等待伺服器B確認。 (2)第二次握手:伺服器B收到SYN包,必須確認客戶A的SYN(ACK=j+1),同時自己也傳送一個SYN包(SYN=k),即SYN+ACK包,此時伺服器B進入SYN_RECV狀態。

為什麼TCP建立連線協議是三握手,而關閉連線卻是握手呢?

看到了一道面試題:“為什麼TCP建立連線協議是三次握手,而關閉連線卻是四次握手呢?為什麼不能用兩次握手進行連線?”,想想最近也到金三銀四了,所以就查閱了相關資料,整理出來了這篇文章,希望對你們有所幫助。 TCP 連線 我們先來補一下基礎什麼是 TCP 協議?傳輸控制協議( Transmission Contr

tcp協議報文和三握手揮手

tcp報文 三次握手與四次揮手 tcp11種狀態tcp協議:tcp是面向連接、可靠的進程到進程之間的協議。tcp提供全雙工服務:即:數據可在同一時間雙向傳輸。tcp報文段首部格式:各字段含義:源端口號:16位字段,為發送端進程對應的端口號目標端口:16位字段,為接收端進程對應的端口號,接收方接收到數據

【轉】TCP建立連接三握手和釋放連接握手

eight 請求 置1 計時器 響應 發送數據 出現 期望 本地 在談及TCP建立連接和釋放連接過程,先來簡單認識一下TCP報文段首部格式的的幾個名詞(這裏只是簡單說明,具體請查看相關教程) 序列號seq:占4個字節,用來標記數據段的順序,TCP把連接中發送的所有數

為什麽TCP連接需要三握手分開需要握手

發送數據 回復 兩件 請求 com 可靠性 網絡 浪費 準備工作 原文地址:http://lixiangfeng.com/blog/article/content/7908246 TCP的三次握手和四次斷開TCP是一個面向連接的服務,面向連接的服務是電話系統服務模式的抽象,

TCP關閉的握手

logs nbsp -1 握手 cnblogs eight images 返回 client 主動方(Client);http為服務器,close--->調用tcp的FIN關鍵字 被動方寫入EOF,然後返回ACK確認,再close-->tcp發送FIN關閉 主

TCP協議中的三握手揮手(圖解)(轉)

繼續 丟失 get 所有 如果 idt 請求報文 網絡 center 轉自:http://blog.csdn.net/whuslei/article/details/6667471 建立TCP需要三次握手才能建立,而斷開連接則需要四次握手。整個過程如下圖所示: 先來看看如