三次握手

三次握手涉及的核心函數(shù)調用
Server::socket, bind, listen
Client::socket, connect
便于理解的一些解釋
關于三次握手, 幾句話解釋清楚
1.信道不安全 保證通信需要一來一回
2.客戶端的來回和服務端的來回 共四次 這是最多四次
3.客戶端的回和服務端的來合并成一個,就是那個SYN k 和 ACK j+1
4.這樣就是三次握手
為什么需要三次握手,不是四次?
tcp 的連接需要確保雙方收和發(fā)消息的能力都是正常的。
客戶端第一次發(fā)送握手 SYN 消息 j 到服務端, 服務端收到握手 SYN 消息 j 后把自己 的握手消息 SYN k 和 握手應答 ACK j+1 一并發(fā)送給客戶端, 這是第二次握手,
當客戶端收到服務端送來的第二次握手消息后,客戶端可以確認“服務端的收發(fā)能力都OK,客戶端的收發(fā)能力也OK”,
但是服務端只能確認“客戶端的發(fā)送OK,服務端的接受OK”,
所以還需要第三次握手, 客戶端收到服務端的第二次握手消息后,發(fā)起第三次ACK k+1 ,服務端收到第三次消息后,就能夠確定“服務端的發(fā)送OK, 客戶端的接收OK”,
至此,客戶端和服務的都能夠確認自己和對方的收發(fā)能力OK, tcp 連接建立完成。
四次揮手
流程概述
假設 發(fā)起端 主機A,接收端 主機B
- A 發(fā)送 FIN 報文 m , 進入FIN_WAIT_1
- B 回復 ACK 報文 m + 1 ,進入 CLOSE_WAIT 狀態(tài)
- A 收到 ACK m + 1 , 進入FIN_WAIT_2
- 同時,B 通過read 調用獲得 EOF,并將此結果通知應用程序進行主動關閉操作,發(fā)送 FIN 報文 n
- ?A 回復 ACK n + 1 ,進入 TIME_WAIT
- B 進入 CLOSED
通常在 TIME_WAIT 停留持續(xù)時間是固定的,是最長分節(jié)生命期 MSL(maximum segment lifetime)的兩倍,一般稱之為 2MSL。和大多數(shù) BSD 派生的系統(tǒng)一樣,Linux 系統(tǒng)里有一個硬編碼的字段,名稱為TCP_TIMEWAIT_LEN,其值為 60 秒。也就是說,Linux 系統(tǒng)停留在 TIME_WAIT 的時間為固定的 60 秒。

只有發(fā)起連接終止的一方會進入 TIME_WAIT 狀態(tài),現(xiàn)在我們來思考一下 TIME_WAIT 的作用。
為什么不直接進入 CLOSED 狀態(tài),而要停留在 TIME_WAIT 這個狀態(tài)?
這樣做是為了確保最后的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。
舉個例子:以防一些情況下TCP 報文傳輸會出錯,需要重傳,如果ACK 傳輸失敗,那么FIN 報文會被對端再次發(fā)出,若沒有維護 TIME_WAIT 狀態(tài),直接進入CLOSED 狀態(tài),就失去了當前狀態(tài)的上下文,只能回復RST 報文,從而導致被動關閉方出現(xiàn)錯誤。防止延時的報文‘遲到’, 此時舊連接已經不存在,但是恰巧有四元組相同的新連接,這個時候報文會被誤發(fā),對TCP 通信產生影響。所以,按照TCP 設計的規(guī)范,經過2MSL 的時間,舊報文就會被丟棄,從而解決上述這個問題。
2MSL 的時間是從主機 1 接收到 FIN 后發(fā)送 ACK 開始計時的;如果在 TIME_WAIT 時間內,因為主機 1 的 ACK 沒有傳輸?shù)街鳈C 2,主機 1 又接收到了主機 2 重發(fā)的 FIN 報文,那么 2MSL 時間將重新計時。道理很簡單,因為 2MSL 的時間,目的是為了讓舊連接的所有報文都能自然消亡,現(xiàn)在主機 1 重新發(fā)送了 ACK 報文,自然需要重新計時,以便防止這個 ACK 報文對新可能的連接化身造成干擾。
TIME_WAIT 的壞處
內存資源占用, 這個問題幾乎可以忽略
對端口資源的占用, 一個 TCP 連接至少消耗一個本地端口。要知道,端口資源也是有限的,一般可以開啟的端口為 32768~61000 ,也可以通過net.ipv4.ip_local_port_range 指定,如果 TIME_WAIT 狀態(tài)過多,會導致無法創(chuàng)建新連接。
如何優(yōu)化 TIME_WAIT ?
- net.ipv4.tcp_max_tw_buckets 默認為18000 , 這個值的含義是,當系統(tǒng)中處于 TIME_WAIT 的連接一旦超過這個值時,系統(tǒng)就會將所有 TIME_WAIT 連接狀態(tài)重置,并打印出警告信息。不過這個方法過于暴力,治標不治本,帶來的問題遠比解決的問題多,不推薦使用。
調低 TCP_TIMEWAIT_LEN,重新編譯系統(tǒng),需要一些內核方面的知識,操作起來有一定的困難。
設置 SO_LINGER ,比較危險,暫不詳細闡述
net.ipv4.tcp_tw_reuse
那么 Linux 有沒有提供更安全的選擇呢?
就是net.ipv4.tcp_tw_reuse選項。
Linux 系統(tǒng)對于net.ipv4.tcp_tw_reuse的解釋如下:
Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0.It should not be changed without advice/request of technical experts.
這段話的大意是從協(xié)議角度理解如果是安全可控的,可以復用處于 TIME_WAIT 的套接字為新的連接所用。
那么什么是協(xié)議角度理解的安全可控呢?
主要有兩點:
- 只適用于連接發(fā)起方(C/S 模型中的客戶端);
- 對應的 TIME_WAIT 狀態(tài)的連接創(chuàng)建時間超過 1 秒才可以被復用。
使用這個選項,還有一個前提,需要打開對 TCP 時間戳的支持,即net.ipv4.tcp_timestamps=1(默認即為 1)。
要知道,TCP 協(xié)議也在與時俱進,RFC 1323 中實現(xiàn)了 TCP 拓展規(guī)范,以便保證 TCP 的高可用,并引入了新的 TCP 選項,兩個 4 字節(jié)的時間戳字段,用于記錄 TCP 發(fā)送方的當前時間戳和從對端接收到的最新時間戳。由于引入了時間戳,我們在前面提到的 2MSL 問題就不復存在了,因為重復的數(shù)據(jù)包會因為時間戳過期被自然丟棄。
總結
- TIME_WAIT 的引入是為了讓 TCP 報文得以自然消失,同時為了讓被動關閉方能夠正常關閉;
- 不要試圖使用SO_LINGER設置套接字選項,跳過 TIME_WAIT;
- 現(xiàn)代 Linux 系統(tǒng)引入了更安全可控的方案,可以幫助我們盡可能地復用 TIME_WAIT 狀態(tài)的連接。