TCP 通信協議

TCP 是面向連接的、可靠的、基於字節流的傳輸層通信協議:

TCP 頭部格式

網絡模型

談一談你對 TCP/IP 四層模型,OSI 七層模型的理解?

OSI 參考模型

OSI(Open System Interconnect),即開放式系統互聯。一般都叫 OSI 參考模型,是 ISO(國際標準化組織)組織在 1985 年研究的網絡互連模型。ISO 爲了更好的使網絡應用更爲普及,推出了 OSI 參考模型。其含義就是推薦所有公司使用這個規範來控制網絡。這樣所有公司都有相同的規範,就能互聯了。

微信搜索公衆號:架構師指南,回覆:架構師 領取資料 。

TCP/IP 五層模型

TCP/IP 五層協議和 OSI 的七層協議對應關係如下:

TCP 狀態

如何在 Linux 系統中查看 TCP 狀態?

TCP 的連接狀態查看,在 Linux 可以通過 netstat -napt 命令查看:

TIME_WAIT

① 爲什麼需要 TIME_WAIT 狀態?

主動發起關閉連接的一方,纔會有 TIME-WAIT 狀態。需要 TIME-WAIT 狀態,主要是兩個原因:

② TIME_WAIT 過多有什麼危害?

如果服務器有處於 TIME-WAIT 狀態的 TCP,則說明是由服務器方主動發起的斷開請求。過多的 TIME-WAIT 狀態主要的危害有兩種:

第二個危害是會造成嚴重的後果的,要知道,端口資源也是有限的,一般可以開啓的端口爲 32768~61000,也可以通過如下參數設置指定

net.ipv4.ip_local_port_range

如果發起連接一方的 TIME_WAIT 狀態過多,佔滿了所有端口資源,則會導致無法創建新連接。

客戶端受端口資源限制:

服務端受系統資源限制:

③ 如何優化 TIME_WAIT?

這裏給出優化 TIME-WAIT 的幾個方式,都是有利有弊:

④ 爲什麼 TIME_WAIT 等待的時間是 2MSL?

MSL 是 Maximum Segment Lifetime,報文最大生存時間,它是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。因爲 TCP 報文基於是 IP 協議的,而 IP 頭中有一個 TTL 字段,是 IP 數據報可以經過的最大路由數,每經過一個處理他的路由器此值就減 1,當此值爲 0 則數據報將被丟棄,同時發送 ICMP 報文通知源主機。

MSL 與 TTL 的區別:MSL 的單位是時間,而 TTL 是經過路由跳數。所以 MSL 應該要大於等於 TTL 消耗爲 0 的時間,以確保報文已被自然消亡。

TIME_WAIT 等待 2 倍的 MSL,比較合理的解釋是:網絡中可能存在來自發送方的數據包,當這些發送方的數據包被接收方處理後又會向對方發送響應,所以一來一回需要等待 2 倍的時間。

比如如果被動關閉方沒有收到斷開連接的最後的 ACK 報文,就會觸發超時重發 Fin 報文,另一方接收到 FIN 後,會重發 ACK 給被動關閉方, 一來一去正好 2 個 MSL。

2MSL 的時間是從客戶端接收到 FIN 後發送 ACK 開始計時的。如果在 TIME-WAIT 時間內,因爲客戶端的 ACK 沒有傳輸到服務端,客戶端又接收到了服務端重發的 FIN 報文,那麼 2MSL 時間將重新計時。

在 Linux 系統裏 2MSL 默認是 60 秒,那麼一個 MSL 也就是 30 秒。Linux 系統停留在 TIME_WAIT 的時間爲固定的 60 秒。

其定義在 Linux 內核代碼裏的名稱爲 TCP_TIMEWAIT_LEN:

#define TCP_TIMEWAIT_LEN (60HZ) / how long to wait to destroy TIME-WAIT  state, about 60 seconds  */

如果要修改 TIME_WAIT 的時間長度,只能修改 Linux 內核代碼裏 TCP_TIMEWAIT_LEN 的值,並重新編譯 Linux 內核。

連接過程

TCP 三次握手

開始客戶端和服務器都處於 CLOSED 狀態,然後服務端開始監聽某個端口,進入 LISTEN 狀態:

第三次握手是否可以攜帶數據?

第三次握手是可以攜帶數據的,前兩次握手是不可以攜帶數據的。一旦完成三次握手,雙方都處於 ESTABLISHED 狀態,此時連接就已建立完成,客戶端和服務端就可以相互發送數據了。假設第三次握手的報文的 seq 是 x+1:

① 服務端 SYN-RECV 流程

② 客戶端 SYN-SEND 流程

爲什麼是三次握手?不是兩次、四次?

TCP 建立連接時,通過三次握手能防止歷史連接的建立,能減少雙方不必要的資源開銷,能幫助雙方同步初始化序列號。序列號能夠保證數據包不重複、不丟棄和按序傳輸。不使用「兩次握手」和「四次握手」的原因:

接下來以三個方面分析三次握手的原因:

原因一:避免歷史連接

客戶端連續發送多次 SYN 建立連接的報文,在網絡擁堵情況下:

如果是兩次握手連接,就不能判斷當前連接是否是歷史連接,三次握手則可以在客戶端(發送方)準備發送第三次報文時,客戶端因有足夠的上下文來判斷當前連接是否是歷史連接:

所以,TCP 使用三次握手建立連接的最主要原因是防止歷史連接初始化了連接。

原因二:同步雙方初始序列號

TCP 協議的通信雙方, 都必須維護一個「序列號」, 序列號是可靠傳輸的一個關鍵因素,它的作用:

可見,序列號在 TCP 連接中佔據着非常重要的作用,所以當客戶端發送攜帶「初始序列號」的 SYN 報文的時候,需要服務端回一個 ACK 應答報文,表示客戶端的 SYN 報文已被服務端成功接收,那當服務端發送「初始序列號」給客戶端的時候,依然也要得到客戶端的應答迴應,這樣一來一回,才能確保雙方的初始序列號能被可靠的同步。

四次握手其實也能夠可靠的同步雙方的初始化序號,但由於第二步和第三步可以優化成一步,所以就成了「三次握手」。而兩次握手只保證了一方的初始序列號能被對方成功接收,沒辦法保證雙方的初始序列號都能被確認接收。

原因三:避免資源浪費

如果只有「兩次握手」,當客戶端的 SYN 請求連接在網絡中阻塞,客戶端沒有接收到 ACK 報文,就會重新發送 SYN ,由於沒有第三次握手,服務器不清楚客戶端是否收到了自己發送的建立連接的 ACK 確認信號,所以每收到一個 SYN 就只能先主動建立一個連接,這會造成什麼情況呢?如果客戶端的 SYN 阻塞了,重複發送多次 SYN 報文,那麼服務器在收到請求後就會建立多個冗餘的無效鏈接,造成不必要的資源浪費。

即兩次握手會造成消息滯留情況下,服務器重複接受無用的連接請求 SYN 報文,而造成重複分配資源。

TCP 四次揮手

四次揮手過程:

爲什麼揮手需要四次?

再來回顧下四次揮手雙方發 FIN 包的過程,就能理解爲什麼需要四次了。

從上面過程可知,服務端通常需要等待完成數據的發送和處理,所以服務端的 ACK 和 FIN 一般都會分開發送,從而比三次握手導致多了一次。

TCP 優化

正確有效的使用 TCP 參數可以提高 TCP 性能。以下將從三個角度來闡述提升 TCP 的策略,分別是:

TCP 三次握手優化

TCP 四次揮手優化

TCP 數據傳輸優化

常見問題

TCP 和 UDP

TCP 和 UDP 的區別?

ISN

① 爲什麼客戶端和服務端的初始序列號 ISN 是不相同的?

如果一個已經失效的連接被重用了,但是該舊連接的歷史報文還殘留在網絡中,如果序列號相同,那麼就無法分辨出該報文是不是歷史報文,如果歷史報文被新的連接接收了,則會產生數據錯亂。所以,每次建立連接前重新初始化一個序列號主要是爲了通信雙方能夠根據序號將不屬於本連接的報文段丟棄。另一方面是爲了安全性,防止黑客僞造的相同序列號的 TCP 報文被對方接收。

② 初始序列號 ISN 是如何隨機產生的?

起始 ISN 是基於時鐘的,每 4 毫秒 + 1,轉一圈要 4.55 個小時。RFC1948 中提出了一個較好的初始化序列號 ISN 隨機生成算法。

ISN = M + F (localhost, localport, remotehost, remoteport)

UDP

總結

TCP 數據可靠性

一句話:通過校驗和、序列號、確認應答、超時重傳、連接管理、流量控制、擁塞控制等機制來保證可靠性。

(1)校驗和

在數據傳輸過程中,將發送的數據段都當做一個 16 位的整數,將這些整數加起來,並且前面的進位不能丟棄,補在最後,然後取反,得到校驗和。

發送方:在發送數據之前計算校驗和,並進行校驗和的填充。接收方:收到數據後,對數據以同樣的方式進行計算,求出校驗和,與發送方進行比較。

(2)序列號

TCP 傳輸時將每個字節的數據都進行了編號,這就是序列號。序列號的作用不僅僅是應答作用,有了序列號能夠將接收到的數據根據序列號進行排序,並且去掉重複的數據。

(3)確認應答

TCP 傳輸過程中,每次接收方接收到數據後,都會對傳輸方進行確認應答,也就是發送 ACK 報文,這個 ACK 報文中帶有對應的確認序列號,告訴發送方,接收了哪些數據,下一次數據從哪裏傳。

(4)超時重傳

在進行 TCP 傳輸時,由於存在確認應答與序列號機制,也就是說發送方發送一部分數據後,都會等待接收方發送的 ACK 報文,並解析 ACK 報文,判斷數據是否傳輸成功。如果發送方發送完數據後,遲遲都沒有接收到接收方傳來的 ACK 報文,那麼就對剛剛發送的數據進行重發。

(5)連接管理

就是指三次握手、四次揮手的過程。

(6)流量控制

如果發送方的發送速度太快,會導致接收方的接收緩衝區填充滿了,這時候繼續傳輸數據,就會造成大量丟包,進而引起丟包重傳等等一系列問題。TCP 支持根據接收端的處理能力來決定發送端的發送速度,這就是流量控制機制。

具體實現方式:接收端將自己的接收緩衝區大小放入 TCP 首部的『窗口大小』字段中,通過 ACK 通知發送端。

(7)擁塞控制

TCP 傳輸過程中一開始就發送大量數據,如果當時網絡非常擁堵,可能會造成擁堵加劇。所以 TCP 引入了慢啓動機制,在開始發送數據的時候,先發少量的數據探探路。

TCP 協議如何提高傳輸效率

一句話:TCP 協議提高效率的方式有滑動窗口、快重傳、延遲應答、捎帶應答等。

(1)滑動窗口

如果每一個發送的數據段,都要收到 ACK 應答之後再發送下一個數據段,這樣的話我們效率很低,大部分時間都用在了等待 ACK 應答上了。

爲了提高效率我們可以一次發送多條數據,這樣就能使等待時間大大減少,從而提高性能。窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值。

(2)快重傳

快重傳也叫高速重發控制。

那麼如果出現了丟包,需要進行重傳。一般分爲兩種情況:

情況一:數據包已經抵達,ACK 被丟了。這種情況下,部分 ACK 丟了並不影響,因爲可以通過後續的 ACK 進行確認;

情況二:數據包直接丟了。發送端會連續收到多個相同的 ACK 確認,發送端立即將對應丟失的數據重傳。

(3)延遲應答

如果接收數據的主機立刻返回 ACK 應答,這時候返回的窗口大小可能比較小。

窗口越大,網絡吞吐量就越大,傳輸效率就越高;我們的目標是在保證網絡不擁塞的情況下儘量提高傳輸效率。

(4)捎帶應答

在延遲應答的基礎上,很多情況下,客戶端服務器在應用層也是一發一收的。這時候常常採用捎帶應答的方式來提高效率,而 ACK 響應常常伴隨着數據報文共同傳輸。如:三次握手。

TCP 如何處理擁塞

網絡擁塞現象是指到達通信網絡中某一部分的分組數量過多,使得該部分網絡來不及處理,以致引起這部分乃至整個網絡性能下降的現象,嚴重時甚至會導致網絡通信業務陷入停頓,即出現死鎖現象。擁塞控制是處理網絡擁塞現象的一種機制。

擁塞控制的四個階段:

Socket

基於 TCP 協議的客戶端和服務器工作:

listen 時候參數 backlog 的意義?

Linux 內核中會維護兩個隊列:

未完成連接隊列(SYN 隊列):接收到一個 SYN 建立連接請求,處於 SYN_RCVD 狀態;

已完成連接隊列(Accpet 隊列):已完成 TCP 三次握手過程,處於 ESTABLISHED 狀態;

SYN 隊列 與 Accpet 隊列

int listen (int socketfd, int backlog)

參數一 socketfd 爲 socketfd 文件描述符

參數二 backlog,這參數在歷史內環版本有一定的變化

在早期 Linux 內核 backlog 是 SYN 隊列大小,也就是未完成的隊列大小。在 Linux 內核 2.2 之後,backlog 變成 accept 隊列,也就是已完成連接建立的隊列長度,所以現在通常認爲 backlog 是 accept 隊列。但是上限值是內核參數 somaxconn 的大小,也就說 accpet 隊列長度 = min(backlog, somaxconn)。

accept 發送在三次握手的哪一步?

我們先看看客戶端連接服務端時,發送了什麼?

客戶端的協議棧向服務器端發送了 SYN 包,並告訴服務器端當前發送序列號 client_isn,客戶端進入 SYNC_SENT 狀態

服務器端的協議棧收到這個包之後,和客戶端進行 ACK 應答,應答的值爲 client_isn+1,表示對 SYN 包 client_isn 的確認,同時服務器也發送一個 SYN 包,告訴客戶端當前我的發送序列號爲 server_isn,服務器端進入 SYNC_RCVD 狀態

客戶端協議棧收到 ACK 之後,使得應用程序從 connect 調用返回,表示客戶端到服務器端的單向連接建立成功,客戶端的狀態爲 ESTABLISHED,同時客戶端協議棧也會對服務器端的 SYN 包進行應答,應答數據爲 server_isn+1

應答包到達服務器端後,服務器端協議棧使得 accept 阻塞調用返回,這個時候服務器端到客戶端的單向連接也建立成功,服務器端也進入 ESTABLISHED 狀態

從上面的描述過程,我們可以得知客戶端 connect 成功返回是在第二次握手,服務端 accept 成功返回是在三次握手成功之後。

客戶端調用 close 了,連接是斷開的流程是什麼?

我們看看客戶端主動調用了 close,會發生什麼?

客戶端調用 close,表明客戶端沒有數據需要發送了,則此時會向服務端發送 FIN 報文,進入 FIN_WAIT_1 狀態

服務端接收到了 FIN 報文,TCP 協議棧會爲 FIN 包插入一個文件結束符 EOF 到接收緩衝區中,應用程序可以通過 read 調用來感知這個 FIN 包。這個 EOF 會被放在已排隊等候的其他已接收的數據之後,這就意味着服務端需要處理這種異常情況,因爲 EOF 表示在該連接上再無額外數據到達。此時服務端進入 CLOSE_WAIT 狀態

接着,當處理完數據後,自然就會讀到 EOF,於是也調用 close 關閉它的套接字,這會使得會發出一個 FIN 包,之後處於 LAST_ACK 狀態

客戶端接收到服務端的 FIN 包,併發送 ACK 確認包給服務端,此時客戶端將進入 TIME_WAIT 狀態

服務端收到 ACK 確認包後,就進入了最後的 CLOSE 狀態

客戶端進過 2MSL 時間之後,也進入 CLOSE 狀態

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ODKvISKeqZePocwhqUv6fQ