自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一個網(wǎng)管的自我修養(yǎng)之TCP協(xié)議

網(wǎng)絡(luò) 通信技術(shù)
TCP 協(xié)議是網(wǎng)絡(luò)傳輸中至關(guān)重要的一個協(xié)議,它位于傳輸層。向上支持 FTP、TELNET、SMTP、DNS、HTTP等常見的應(yīng)用層協(xié)議,向下要與網(wǎng)絡(luò)層的 IP 協(xié)議相互配合,實現(xiàn)可靠的網(wǎng)絡(luò)傳輸。

今天,繼續(xù)來網(wǎng)管的自我修養(yǎng)之TCP協(xié)議,這可是除 IP 協(xié)議外另一個核心協(xié)議了。

圖片

TCP 協(xié)議是網(wǎng)絡(luò)傳輸中至關(guān)重要的一個協(xié)議,它位于傳輸層。向上支持 FTP、TELNET、SMTP、DNS、HTTP等常見的應(yīng)用層協(xié)議,向下要與網(wǎng)絡(luò)層的 IP 協(xié)議相互配合,實現(xiàn)可靠的網(wǎng)絡(luò)傳輸。

分層網(wǎng)絡(luò)模型

OSI 7層模型

為了讓全世界的計算機有效的互聯(lián)起來,國際標準化組織提出了一種概念化的網(wǎng)絡(luò)模型,開放式系統(tǒng)互聯(lián)模型(Open System Interconnection Model),簡稱 OSI 模型。

圖片

自上而下依次為應(yīng)用層、表示層、會話層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層。

應(yīng)用層

應(yīng)用層提供為應(yīng)用軟件而設(shè)計的接口,以設(shè)置與另一應(yīng)用軟件之間的通信。例如:HTTP、HTTPS、FTP、Telnet、SSH、SMTP、POP3等。

表示層

表示層把數(shù)據(jù)轉(zhuǎn)換為能與接收者的系統(tǒng)格式兼容并適合傳輸?shù)母袷健?/p>

會話層

會話層負責在數(shù)據(jù)傳輸中設(shè)置和維護計算機網(wǎng)絡(luò)中兩臺計算機之間的通信連接。

傳輸層

傳輸層把傳輸表頭(TH)加至數(shù)據(jù)以形成數(shù)據(jù)包。傳輸表頭包含了所使用的協(xié)議等發(fā)送信息。例如:傳輸控制協(xié)議(TCP)等。

網(wǎng)絡(luò)層

網(wǎng)絡(luò)層決定數(shù)據(jù)的路徑選擇和轉(zhuǎn)寄,將網(wǎng)絡(luò)表頭(NH)加至數(shù)據(jù)包,以形成分組。網(wǎng)絡(luò)表頭包含了網(wǎng)絡(luò)資料。例如:互聯(lián)網(wǎng)協(xié)議(IP)等。

數(shù)據(jù)鏈路層

數(shù)據(jù)鏈路層負責網(wǎng)絡(luò)尋址、錯誤偵測和改錯。當表頭和表尾被加至數(shù)據(jù)包時,會形成信息框。數(shù)據(jù)鏈表頭(DLH)是包含了物理地址和錯誤偵測及改錯的方法。數(shù)據(jù)鏈表尾(DLT)是一串指示數(shù)據(jù)包末端的字符串。例如以太網(wǎng)、無線局域網(wǎng)(Wi-Fi)和通用分組無線服務(wù)(GPRS)等。

分為兩個子層:邏輯鏈路控制(logical link control,LLC)子層和介質(zhì)訪問控制(Media access control,MAC)子層。

物理層

物理層在局部局域網(wǎng)上傳送數(shù)據(jù)幀,它負責管理電腦通信設(shè)備和網(wǎng)絡(luò)媒體之間的互通。包括了針腳、電壓、線纜規(guī)范、集線器、中繼器、網(wǎng)卡、主機接口卡等。

OSI 模型是國際標準模型,是指導(dǎo)互聯(lián)網(wǎng)模型的概念標準。而在實際的設(shè)計實現(xiàn)過程中,最后形成了 TCP/IP 4層模型結(jié)構(gòu)。

TCP/IP 4層模型

TCP/IP 模型實際上并不單單指 TCP 和 IP,實際上這一個協(xié)議簇,還包含了其他的一些協(xié)議,比如 UDP、ICMP、IGMP 等。

TCP/IP 模型是事實上的標準模型,在 7 層模型的基礎(chǔ)上將最上面三層的應(yīng)用層、表示層、會話層統(tǒng)一為應(yīng)用層,將數(shù)據(jù)鏈路層和物理層統(tǒng)一為鏈路層或者叫網(wǎng)絡(luò)接口層。

圖片

實際應(yīng)用中還是以 4 層模型為準,畢竟這才是事實上的標準。還有一種 5 層模型的說法,實際上就是把 7 層中的應(yīng)用層、表示層、會話層合并為應(yīng)用層,其他層保持不變。

數(shù)據(jù)的加工和傳輸過程

TCP/IP 模型每個層都有各自的功能和分工,當有用戶數(shù)據(jù)想要發(fā)送給另一臺設(shè)備的時候,數(shù)據(jù)自上而下,從應(yīng)用層向鏈路層傳遞有一個復(fù)雜的過程。

以 Telnet 為例,Telnet 在傳輸層是使用 TCP 協(xié)議的。

數(shù)據(jù)從應(yīng)用層進入,到達傳輸層,添加上 TCP首部,將數(shù)據(jù)加工成 TCP  段,稱為 Segment。這是為了保證數(shù)據(jù)的可靠性。

接著數(shù)據(jù)到達網(wǎng)絡(luò)層,在網(wǎng)絡(luò)層使用 IP 協(xié)議,被添加上 IP 首部,將數(shù)據(jù)加工成 IP數(shù)據(jù)報,稱為 datagram 。經(jīng)過網(wǎng)絡(luò)層 IP 協(xié)議的加工,指定目標地址和 MAC 地址,保證數(shù)據(jù)準確的發(fā)送到目標機器。

接著數(shù)據(jù)到達鏈路層,添加上以太網(wǎng)頭部,將數(shù)據(jù)加工成以太網(wǎng)幀,稱為 frame,包含了網(wǎng)卡等硬件相關(guān)的數(shù)據(jù)。

圖片

無論是 Telnet 還是  HTTP,都至少涉及到兩臺設(shè)備才能稱之為網(wǎng)絡(luò)互連,那發(fā)送方有一個數(shù)據(jù)自應(yīng)用層向底層鏈路層的加工過程,對應(yīng)的,在數(shù)據(jù)接收方,有一個數(shù)據(jù)從鏈路層向應(yīng)用層解析的過程。這中間可能經(jīng)歷了漫長的傳輸介質(zhì),比如光纖,還可能有若干個中間設(shè)備,比如路由器、交換機等等。要保證數(shù)據(jù)在這么復(fù)雜的網(wǎng)絡(luò)環(huán)境中可靠、準確的發(fā)送到目標機器,就是靠的 TCP、IP協(xié)議精巧的設(shè)計。

圖片

TCP 協(xié)議

TCP,全稱是 Transmission Control Protocol,傳輸控制協(xié)議。是一種面向連接的、可靠的字節(jié)流服務(wù)協(xié)議,正因為它要保證可靠性,所以比起 UDP 協(xié)議要復(fù)雜的多,正是由于這種復(fù)雜性,導(dǎo)致它的性能比 UDP 差。

TCP 是 TCP/IP 模型中的傳輸層一個最核心的協(xié)議,不僅如此,在整個 4 層模型中,它都是核心的協(xié)議,要不然模型怎么會叫做 TCP/IP 模型呢。

它向下使用網(wǎng)絡(luò)層的 IP 協(xié)議,向上為 FTP、SMTP、POP3、SSH、Telnet、HTTP 等應(yīng)用層協(xié)議提供支持。其他的還有我們常用的 Redis 的 RESP 協(xié)議、MongoDB的網(wǎng)絡(luò)協(xié)議,以及我們編程中用到的 Socket,都是 TCP 協(xié)議在背后提供支持的。

網(wǎng)絡(luò)協(xié)議是通信計算機雙方必須共同遵從的一組約定。如怎么樣建立連接、怎么樣互相識別等。只有遵守這個約定,計算機之間才能相互通信交流。它的三要素是:語法、語義、時序。

  1. 語法:即數(shù)據(jù)與控制信息的結(jié)構(gòu)或格式;
  2. 語義:即需要發(fā)出何種控制信息,完成何種動作以及做出何種響應(yīng);
  3. 時序(同步):即事件實現(xiàn)順序的詳細說明。

TCP 協(xié)議格式

圖片

TCP首部 + 用戶數(shù)據(jù)被稱為TCP段,其中 TCP 首部就是這里要主要研究的 TCP 協(xié)議的核心所在,用戶數(shù)據(jù)部分是 TCP 段的負載。

TCP 段的大小也是有限制的,最大是 1460 字節(jié),這是怎么算出的呢?

圖片

最終由網(wǎng)卡發(fā)出去的數(shù)據(jù)包叫做以太網(wǎng)幀,以太網(wǎng)幀由以太網(wǎng)首部和負載構(gòu)成。

以太網(wǎng)幀的負載就是一個 IP 數(shù)據(jù)報,IP數(shù)據(jù)報由IP首部和負載構(gòu)成。

IP數(shù)據(jù)報的負載就是一個 TCP段。所以,TCP段所能搭載的最大數(shù)據(jù)量可以這樣計算出來:

段搭載的數(shù)據(jù)大小以太網(wǎng)幀大小以太網(wǎng)首部首部首部

以太網(wǎng)幀的大小是固定的 1522字節(jié),而IP首部和TCP首部的大小是不固定的,但是最少會各占20字節(jié),所以最后算下來 TCP段搭載的數(shù)據(jù)大小最多為 1460字節(jié)。

TCP段搭載的數(shù)據(jù)大小(最多1460) = 以太網(wǎng)幀大?。?522字節(jié))-以太網(wǎng)首部(22字節(jié))-IP首部(最少20字節(jié))-TCP首部(最少20字節(jié))

下圖是TCP協(xié)議的示意圖,如果不算「可選項」部分的話,共占用 32bit x 5 = 160bit,也就是20個字節(jié)。

圖片

源端口和目標端口

源端口和目標端口分別占用 2個字節(jié),共占用 4 字節(jié),分別記錄數(shù)據(jù)發(fā)送端的端口號和數(shù)據(jù)接收端的端口號,這兩個標記和 IP 協(xié)議中記錄的發(fā)送端 IP 和接收端 IP組合起來,便可確定一個唯一的 TCP 連接。

序號

由于TCP段的大小有限制,當要傳輸?shù)臄?shù)據(jù)量大于這個限制的時候,就要對數(shù)據(jù)進行分段,一段一段的發(fā)送,既然發(fā)送方要分段,那接收方就要對分段進行重組,才能還原回原始數(shù)據(jù)。在重組的過程中,要保證各段間的先后順序,序號正是起到保證重組順序的作用。

序號占用 4 字節(jié),32 位,它的范圍是 [0,]。TCP是字節(jié)流服務(wù),會對每一個發(fā)送的字節(jié)進行編號。在建立連接的時候,系統(tǒng)會給定一個 ISN(初始序號),然后這個設(shè)備在當前連接中發(fā)送的第一個字節(jié)的序號就是 ISN+1,假設(shè) ISN 初始為0,那第一個字節(jié)的序號就是 1。

舉個例子,假設(shè)ISN為0,發(fā)送端第一次發(fā)送 100 字節(jié)的數(shù)據(jù)包,那這第一個 TCP段的序號就是1,下次再發(fā)送 100字節(jié)的數(shù)據(jù)包,那這第二個 TCP段的序號就是 101。

這樣一來,最大可以一直標記 個字節(jié),也就是 4個G的數(shù)據(jù)。當達到最大值后,又會從 0 開始標記。

序號只有在下面兩種情況下才有用:

  1. 數(shù)據(jù)字段至少包含一個字節(jié)。
  2. 是一個 SYN 段,或者是 FIN 段,或者是 RST 段。

確認序號

當數(shù)據(jù)發(fā)送出去,接收方收到之后,會回復(fù)一個確認序號回復(fù)給發(fā)送方,這個確認序號表示接收方希望下次接收的序號。例如發(fā)送了序號為501的,長度為100的TCP段,那接收方收到后要回復(fù) 601的確認序號,表示【0-600】的字節(jié)已經(jīng)接收,下次希望收到第 601個字節(jié)以后的數(shù)據(jù)。

為了提高效率,并不是每次接收到TCP段都會馬上回復(fù)給發(fā)送方,而是采用累積確認的方式,即每傳送多個連續(xù) TCP 段,可以只對最后一個 TCP 段進行確認。

確認序號只有在 ACK 標志位被設(shè)置的時候才有效。

首部長度

之所以需要首部長度,是因為可選項的大小是不固定的,如果沒有可選項的話,那首部長度就是 20字節(jié)。這個標示部分占 4 bit,單位是4字節(jié),4bit 可表示的最大值是 15,一個單位表示的長度是4字節(jié),所以首部長度最大可以是 15 x 4字節(jié),也就是 60 字節(jié)。

圖片

保留

顧名思義,是保留位,占用6個比特位,目前的值為 0。

6個標志位

協(xié)議中有 6 個比特標記位,可以理解為 TCP 段的類型。

URG

1個比特位,當被設(shè)置為1時,表明緊急指針字段有效,該報文段有緊急數(shù)據(jù),應(yīng)盡快發(fā)送。

ACK

當 ACK 設(shè)置為1時,確認號才有效,連接建立后,所有的報文段ACK都為 1。

PSH

當 PSH 設(shè)置為1時,接收方應(yīng)該盡快將這個報文段交給應(yīng)用層,而不再等待整個緩存填滿再交付。

RST

當 RST 為1時,表示連接出現(xiàn)嚴重錯誤,必須重新建立連接。

SYN

在建立連接時用到。

當SYN=1,ACK=0時,表明這是一個連接請求報文段。

當SYN=1,ACK=1時,表明對方同意連接。

FIN

用來釋放一個連接窗口。當FIN=1時,表明此報文段的發(fā)送方不再發(fā)送數(shù)據(jù),請求釋放單向連接。TCP斷開連接用到。

窗口大小

大小為2個字節(jié),表示發(fā)送方自己的接收窗口,用來告訴對方允許發(fā)送的數(shù)據(jù)量,最大為65535字節(jié)。

檢驗和

校驗和是必需的,是一個端到端的校驗和,由發(fā)送端計算,然后由接收端驗證。其目的是為了發(fā)現(xiàn)TCP首部和數(shù)據(jù)在發(fā)送端到接收端之間發(fā)生的任何改動。如果接收方檢測到校驗和有差錯,則TCP段會被直接丟棄。

緊急指針

占2字節(jié),當URG=1時,緊急指針表示本報文段中的緊急數(shù)據(jù)的字節(jié)數(shù),表示從這個 TCP段的序號開始的后的若干個字節(jié)是緊急數(shù)據(jù),之后的就是普通數(shù)據(jù)。

假設(shè)此TCP段的序號為101,緊急指針為30,那就表示從 101開始,直到 131,【101,131】這個區(qū)間內(nèi)為緊急數(shù)據(jù)。

三次握手和四次揮手

數(shù)據(jù)要完成傳輸,必須要建立連接。由于建立TCP連接的過程需要來回3次,所以,將這個過程形象的叫做三次握手。

而連接斷開的時候要經(jīng)過四次數(shù)據(jù)傳輸,所以也被稱為4次揮手。

啥都別說了,先看圖吧。

圖片

三次握手,建立連接

結(jié)合上面的圖來看更清楚。

先說三次握手吧,連接是后續(xù)數(shù)據(jù)傳輸?shù)幕A(chǔ)。就像我們打電話一樣,必須保證我和對方都拿著電話在聽,才能保證我們兩個說的話對方能夠接收到。

三次握手大概就是這個意思:

張三想跟李四聊聊天,于是張三撥通了李四的手機號,李四聽到鈴聲響起,按了接聽按鈕。

張三:Hi,李四,是你嗎?嘮兩塊錢的呀!

李四:Hi,張三,是我,可以嘮。

張三:好,我確定是你了,接下來我要開始和你嘮了。

看上去多少有點兒死板,但程序上確實就是這樣的。

1、第一次握手

首先客戶端發(fā)起連接請求,向服務(wù)器發(fā)送 TCP段,段中包含了目標端口和本機端口,設(shè)置 SYN 標志位為1,序號為 x,也就是初始序號 ISN,如果是第一個連接,很有可能就是 0。當然,此時服務(wù)器對應(yīng)的端口要處于監(jiān)聽狀態(tài)。此時,客戶端進入 SYNC_SENT 狀態(tài),等待服務(wù)器的確認。

2、第二次握手

服務(wù)端收到客戶端發(fā)來的 SYN 段,對這個SYN報文段進行確認,設(shè)置Acknowledgment Number為x+1(Sequence Number+1),這就是確認序號。同時,服務(wù)端還要發(fā)送 SYN 請求信息,將SYN位置為1,Sequence Number為 y(服務(wù)端的TCP段序號)。服務(wù)器端將上述所有信息放到一個TCP段(即SYN+ACK段)中,一并發(fā)送給客戶端,此時服務(wù)器進入SYN_RECV狀態(tài)。

3、第三次握手

客戶端接收到服務(wù)端發(fā)來的 SYN+ACK 段后,發(fā)送一個 ACK 給服務(wù)端,將 Acknowledgment Number 設(shè)置為 y+1。此時客戶端進入 ESTABLISHED(已連接)狀態(tài),服務(wù)端接收到此 TCP段,也將進入 ESTABLISHED 狀態(tài),也就標志著三次握手結(jié)束,連接成功建立。

三次握手完成之后,連接就建立了,之后就可以愉快的傳輸數(shù)據(jù)了。

四次揮手,江湖再見

一旦有了感情(連接),再分手就難了,難到需要四次揮手。不像 UDP 那樣,沒有連接,說分就分。

當客戶端和服務(wù)端雙方發(fā)送數(shù)據(jù)完成后,一般會由客戶端主動發(fā)起斷開連接的請求,當然,也有少數(shù)情況是服務(wù)端主動發(fā)起。

以最常見的客戶端發(fā)起斷開連接為例,說一下四次揮手的過程。

1、第一次揮手

客戶端設(shè)置序號(Sequence Number)和確認序號(Acknowledgment Number),發(fā)送一個 FIN 段給服務(wù)器。這時,客戶端進入 FIN_WAIT_1狀態(tài),意味著客戶端沒有數(shù)據(jù)要發(fā)送了。

2、第二次揮手

服務(wù)端收到 FIN 報文段,向客戶端發(fā)送一個 ACK 段,客戶端進入 FIN_WAIT_2 狀態(tài)。表示服務(wù)端已同意連接關(guān)閉請求。

3、第三次揮手

服務(wù)端向客戶端發(fā)送 FIN 段,請求關(guān)閉連接,同時服務(wù)端進入 LAST_ACK 狀態(tài)。

4、第四次揮手

客戶端收到服務(wù)端發(fā)來的 FIN 段,向服務(wù)端發(fā)送 ACK 段,之后客戶端進入TIME_WAIT狀態(tài)。服務(wù)端收到客戶端的ACK 段以后,就關(guān)閉連接。

上面就是由客戶端主動發(fā)起關(guān)閉連接的過程。

半關(guān)閉狀態(tài)

TCP 是一個全雙工的字節(jié)流服務(wù),意思就是說兩個端點都可以同時發(fā)送和接收消息。

正常情況下需要四次揮手才能完成連接的完全斷開。但是有一種情況是這樣的,只主動關(guān)閉自己到對方的連接,但是對方還是可以給自己發(fā)送數(shù)據(jù)。

圖片

用 WireShark 抓住 TCP

Wireshark 是幫助我們分析網(wǎng)絡(luò)請求的利器,建議每個同學(xué)都裝一個。我們先用 Wireshark 抓取一個完整的連接建立、發(fā)送數(shù)據(jù)、斷開連接的過程。

我這兒只簡單的介紹一下操作流程。

1、首先打開 Wireshark,在歡迎界面會列出當前機器上的所有網(wǎng)口、虛機網(wǎng)口等可以抓取的部件。

圖片

2、我接下來要用 Telnet 連接一個外網(wǎng)服務(wù)器,所以我選擇第一個 WI-FI:en0,這樣 Wireshark 就會捕獲我連接的 wifi 上的網(wǎng)絡(luò)傳輸。

3、我只想要抓一下最簡單的 TCP 連接、發(fā)數(shù)據(jù)、斷開的過程,所以要做一下抓取過濾。Wireshark 中的過濾器可以實現(xiàn)這樣的需求。在下圖紅框部分可以選了一個過濾器。

圖片

4、因為當前沒有直接可用的符合要求的過濾器,所以,需要自己寫一個。點擊前面的綠色書簽圖標,然后在彈出窗口中點擊加號添加一個。

圖片

內(nèi)容如下,語法就不解釋了,一看就知道。

tcp and host 你的遠程ip

5、選擇好剛添加的這個過濾器,雙擊wifi這個 interface 進入就開始捕獲了。

6、我用 telnet 連接這臺服務(wù)器的 6379 端口 telnet ip 6379,因為這臺服務(wù)器上裝著 redis,可以模擬發(fā)數(shù)據(jù)。

圖片

在控制臺中連接到 6379 端口成功,然后在 Wireshark 上馬上捕獲到了。

圖片

這就是三次握手的過程。

7、然后直接關(guān)掉終端,這樣會自動觸發(fā)斷開連接,并且發(fā)送最少的數(shù)據(jù),方便我們觀察。整個的過程都被 Wireshark 完整的捕捉到了。

圖片

第一部分是連接建立的三次握手,第二部分是發(fā)了長度為 1個字節(jié)的數(shù)據(jù),第三步是客戶端主動發(fā)起的斷開連接的四次揮手過程。

Wireshark 簡單介紹

有圖先看圖

圖片

概覽信息

也就是圖中最上面的紅色框部分。這一次的連接建立和中斷一共產(chǎn)生了來回 8 次的請求,每次請求會在列表上列出時間、源端IP、目的端IP、以太網(wǎng)幀長度以及概覽信息,包括數(shù)據(jù)傳輸方向(源端口->目標端口)、標記情況、序號、確認序號、窗口大小等等。

以太網(wǎng)幀

在每次請求信息中,還包括以太網(wǎng)幀,因為信息最終都會通過幀的形式發(fā)送出去。

IP數(shù)據(jù)報

還有 IP 數(shù)據(jù)報內(nèi)容,其中包含了源端 IP 和 目的端 IP 等信息。

圖片

TCP段

TCP 段當然是重點了,其中包含了 TCP 協(xié)議中的所有信息,包括端口號、

圖片

粘包、半包

MTU是什么

MTU 全稱是最大傳輸單元,一個在網(wǎng)絡(luò)上傳輸?shù)陌荒軣o限大,MTU 一般是對于鏈路層而言的,拿以太網(wǎng)來說,在鏈路層允許發(fā)送的最大的以太網(wǎng)幀的數(shù)據(jù)部分就是 1500字節(jié)。注意是以太網(wǎng)幀的數(shù)據(jù)部分,再加上以太網(wǎng)幀的頭部,會大于1500字節(jié)。

通過 ifconfig(windows 系統(tǒng)是 ipconfig)可查看本機各個網(wǎng)絡(luò)接口(網(wǎng)卡)的MTU 大小。

圖片

MSS是什么

MSS 指TCP最大報文長度,是TCP協(xié)議定義的一個選項,MSS選項用于在TCP連接建立時,收發(fā)雙方協(xié)商通信時每一個報文段所能承載的最大數(shù)據(jù)長度。還是用以太網(wǎng)為例,MTU是 1500字節(jié),減去TCP頭(20字節(jié))和IP頭(20字節(jié)),就是MSS 1460字節(jié)。

圖片

粘包

粘包就是將幾個比較小的 TCP 包合并成一個包,這樣就只發(fā)送一次就可以將多個小包發(fā)送出去。例如下面這樣,一個TCP報文請求中,包含小包A、B、C,每一個小包原本都是一個TCP報文。

圖片

為什么要粘包呢?一個一個發(fā)送不行嗎?

其實是可以的,只不過在多數(shù)情況下來一個包馬上就發(fā)送可能會造成網(wǎng)絡(luò)擁塞,一個TCP 報文傳輸?shù)芥溌穼拥臅r候,會加上TCP頭和IP頭,占用40字節(jié),如果發(fā)送的數(shù)據(jù)內(nèi)容很小,比如只有1個字節(jié),為了這一字節(jié)的內(nèi)容,要有40倍的額外的信息被傳輸,是不是有點浪費。

為了減少這種浪費,TCP 協(xié)議就做了一些優(yōu)化,比如 Nagle 算法:

  • Nagle 算法規(guī)定每次只有收到上一個包的確認(ACK)之后,才會發(fā)送下一個包,在這個時間段內(nèi)正好將小的包粘到一起;
  • 但是太多的包也不行,大小不能超過 MSS ,也就是前面剛說的 1460字節(jié),太大了裝不下;
  • 如果沒有那么多小包,也不能一直等著,有一個超時時間,大約是200ms,超過這個時間也要發(fā)送;

由于現(xiàn)在的寬帶和設(shè)備性能的提升,Nagle 算法其實可以關(guān)閉了,有些設(shè)備上默認就是關(guān)閉的,也可以在寫 Socket 的代碼的時候主動關(guān)閉掉,關(guān)閉之后呢,只要接收端處理能力夠快,可以保證來一個包馬上發(fā)送,對那些要求實時反饋的應(yīng)用來說尤其重要。

那來一個包發(fā)一個包,是不是就不會有粘包的問題了?也不是,這就要看接收端的處理能力了,接收端會有一個接收緩沖區(qū),來不及被應(yīng)用程序處理的會暫時放到這里,如果應(yīng)用程序處理能力較差,這里還是會出現(xiàn)粘包。

拆包

既然發(fā)生了粘包,就要把這些大包拆成小包。怎么拆分其實都是上層應(yīng)用的事兒了,核心要點就是約定好分隔符。舉個簡單的例子,比如說將包A和包B用一個特殊字符 $分隔開,那應(yīng)用在拆包的時候就要根據(jù)這個特殊字符進行分隔。當然了,真實情況要比這個復(fù)雜的多,如果你用過 Netty,就會發(fā)現(xiàn) Netty 提供了多種處理粘包拆包的方式。

什么是半包

粘包是為了將多個小包變成一個大包,而半包是把超大包拆成小包。比如下圖,假設(shè)包B是一個很大的包,已經(jīng)超過了MSS 了,單單發(fā)送它自己都發(fā)不過去了,所以只能將它拆開,一部分一部分的發(fā)送。

圖片

半包就沒那么復(fù)雜了,純粹是因為單獨的包太大,協(xié)議不支持這么大的包,只能拆開。

這樣一部分一部分的包,到了接收端之后就要將其合并為一個整體,合并也比較簡單,就是如果這個部分包沒有開始或沒有結(jié)束標志,就表示它不是完整的,需要給其找到對應(yīng)的其他部分。

滑動窗口

接收方通告的窗口稱為 offered window,意思就是說我這邊可以接受的最大字節(jié)數(shù)為這么多。例如下圖中的紅框部分為 offered window, 大小為 6 字節(jié),發(fā)送端最大一次只能發(fā)送 6 個字節(jié),要不然接收方就沒有能力接收了。

可用窗口 = offered window - 已經(jīng)發(fā)送但未被確認的字節(jié)大小,這個值由發(fā)送方自己計算。前面說了三次握手,發(fā)送方發(fā)出去包,接收方接到后會反回一個 ACK,發(fā)出去但未收到ACK的數(shù)據(jù)也會占用窗口,表明接收方正在處理,所以,可用窗口的大小是 offered window 減去未收到 ACK 的大小。

圖片

為什么叫滑動窗口呢,看上面的圖,把一個個字節(jié)想象成排成一排的格子。

首先看時刻1:紅色格子的部分就是offered window,大小為6字節(jié),后面10、11、12字節(jié)因為沒在窗口內(nèi),所以不能發(fā)送。已發(fā)送但未被確認的也占用窗口大小,所以最終可用窗口就是 7、8、9這三個字節(jié)。

再看時刻2:剛才未被確認的 4、5、6字節(jié)收到了 ACK,所以1-6都變成了過去式,然后窗口覆蓋到了7、8、9、10、11、12 這6個字節(jié),對比時刻1和時刻2,給我們的感覺就是窗口(紅色格子)向右滑動了,這就是所謂的滑動窗口了。

還有,窗口兩個邊沿的相對運動增加或減少了窗口的大小。

  • 當窗口左邊沿向右邊沿靠近為窗口合攏。這種現(xiàn)象發(fā)生在數(shù)據(jù)被發(fā)送和確認時,假設(shè)現(xiàn)在接收方處理數(shù)據(jù)的時間變長了,來不及快速處理,那接收方在下次ACK的時候返回的窗口大小可能就會變小。

當窗口右邊沿向右移動時將允許發(fā)送更多的數(shù)據(jù), 我們稱之為窗口張開。這種現(xiàn)象發(fā) 生在另一端的接收進程讀取已經(jīng)確認的數(shù)據(jù)并釋放了 TCP的接收緩存時。

慢啟動和擁塞避免算法

在使用 TCP 傳輸?shù)倪^程中,肯定是希望數(shù)據(jù)傳送的越快越好,但是在實際使用場景中,由于發(fā)送端和接收端處理數(shù)據(jù)的速度不一致,或者由于中間路由器性能限制、帶寬限制等原因,發(fā)送的速度越快,越有可能導(dǎo)致丟包的情況。比如一下子發(fā)送了10M的數(shù)據(jù)出來,但是中間路由器只能處理 5 M,很可能就會把一些包丟棄。

因而設(shè)計了慢啟動和擁塞避免算法,這兩個設(shè)計都是為了合理的匹配發(fā)端的發(fā)送速度與收端的處理速度。

慢啟動

在連接剛建立的時候,發(fā)送端也不知道應(yīng)該按什么速度發(fā)比較合適,所以就采用了一種漸進式的方式,就是慢啟動的方式。

前面說了 offered window 是接收端的,在發(fā)送端也有一個窗口,叫做擁塞窗口,記做 cwnd,擁塞窗口初始化為 1 ,表示 1個報文段,也就是允許發(fā)送1個報文段,之后每當每當收到接收端返回的 ACK 時,就將 cwnd 的值加1。第一次發(fā)送一個數(shù)據(jù)報,當收到 ACK 后,cwnd 變?yōu)?,然后下一次發(fā)送兩個數(shù)據(jù)報,當收到這兩個數(shù)據(jù)報的 ACK 時,cwnd 就變成 4 。以此類推,這個增長是呈指數(shù)級的。

但是,在這個過程中,也是有限制的,發(fā)送的數(shù)據(jù)報大小要在消息接收端返回的通告窗口大小和 cwnd 中取較小的那個值。假設(shè)一個報文大小為 1024 字節(jié),當 cwnd 為2,通告窗口大小為 4096 字節(jié)時,那發(fā)送端你可以連著發(fā)送2個數(shù)據(jù)報,也就是取 cwnd 的值,當 cwnd 為8 時,通告窗口大小仍然為 4096 字節(jié)時,那發(fā)送端最多可連續(xù)發(fā)送 4 個數(shù)據(jù)報,也就是不能超過 4096 字節(jié)。

擁塞避免

擁塞避免算法其實和慢啟動是在一起使用的。在慢啟動中除了有擁塞窗口外, 還有一個叫做啟動門限(ssthresh)的參數(shù)。啟動門限默認的是 65535 字節(jié)。

在慢啟動中,cwnd 是呈指數(shù)級增長,但是這個增長速度太快了,所以,擁塞避免算法就是讓這個增速減緩的方式。

當 cwnd < ssthresh 的時候,就使用慢啟動。

當 cwnd > ssthresh 的時候,就啟動擁塞避免算法。

擁塞避免算法保證當 cwnd 超過限制之后,每次收到一個確認時將 cwnd 增加 1/cwnd。

當擁塞發(fā)生時(超時或收到重復(fù)確認),ssthresh 被設(shè)置為當前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少為 2個報文段)。

用一張圖來說明慢啟動和擁塞避免算法

圖片

假定當 cwnd 為32個報文段時就會發(fā)生擁塞。于是設(shè)置 ssthresh 為1 6個報文段, 而 cwnd 為1個報文段。在時刻 0發(fā)送了一個報文段, 并假定在時刻 1接收到它的 ACK,此時 cwnd 增加為2。接著發(fā)送了2個報文段,并假定在時刻 2接收到它們的 ACK,于是 cwnd 增加為4 (對每個 ACK 增加1次)。這種指數(shù)增加算法一直進行到在時刻 3和4之間收到8個A C K后 cwnd 等 于 ssthresh 時才停止,從該時刻起,cwnd 以線性方式增加,在每個往返時間內(nèi)最多增加 1個報 文段。

正如我們在這個圖中看到的那樣, 術(shù)語“慢啟動”并不完全正確。它只是采用了比引起 擁塞更慢些的分組傳輸速率, 但在慢啟動期間進入網(wǎng)絡(luò)的分組數(shù)增加的速率仍然是在增加的。只有在達到 ssthresh 擁塞避免算法起作用時,這種增加的速率才會慢下來。

重傳機制

什么情況下要重傳,當發(fā)送端認為丟包了就要重傳,有兩種情況下發(fā)送端就認為丟包了,于是就會發(fā)起重傳。

超時重傳

發(fā)送端在一段時間(超時時間)后沒有收到發(fā)送端返回的 ACK ,就認為這個包丟了,這個超時時間并不是固定的。

這里面有兩個概念,RTT 和 RTO。

  • RTT(Round Trip Time):往返時延,也就是數(shù)據(jù)包從發(fā)出去到收到對應(yīng) ACK 的時間。RTT 是針對連接的,每一個連接都有各自獨立的 RTT。
  • RTO(Retransmission Time Out):重傳超時,也就是前面說的超時時間。

快速重傳

接收端回復(fù)的 ACK 會帶著包的序號,當接收端重復(fù)三次收到同一個序號的ACK時,就要重傳這個包;

比如下面圖中畫的這樣:

圖片

1、seq=1的包發(fā)過去,接收端ACK=2,表示期望下次出現(xiàn)的序號為2,然后發(fā)送端就發(fā)了 seq=2的包,接收端接到后回復(fù) ACK=3,表示期望下次收到序號為3的包,這是發(fā)送端第一次收到 ACK=3;

2、發(fā)送端繼續(xù)發(fā)送 seq=3 的包,但是這個包可能傳輸?shù)谋容^慢(比如路由選擇的不好),接收端一直沒收到;

3、發(fā)送端先不管,繼續(xù)發(fā)送 seq=4 的包,接收端收到后,回復(fù)ACK,正常情況下應(yīng)該是 ACK=5,但是序號為3的包還沒收到,所以再次回復(fù)ACK=3,這是第二次收到ACK=3;

4、發(fā)送端繼續(xù)不管,接著發(fā)送 seq=5的包,接收端收到后,回復(fù)ACK,正常情況下應(yīng)該是 ACK=6,但是序號為3的包還沒收到,所以再次回復(fù)ACK=3,這是第三次收到ACK=3;

到目前為止,已經(jīng)收到三次 ACK=3了,然后發(fā)送端就重新發(fā)送 seq=3的包,這時候就當做這個包已經(jīng)丟了。這就是快速重傳。

?我是風(fēng)箏,主業(yè) Java 程序員,有時也是用 Python 、React 做一些小東西。喜歡并擅長解決問題、處理 bug。堅持原創(chuàng)干貨輸出,關(guān)注我,一起變優(yōu)秀!

本文轉(zhuǎn)載自微信公眾號「古時的風(fēng)箏」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系公眾號。

責任編輯:武曉燕 來源: 古時的風(fēng)箏
相關(guān)推薦

2020-04-17 10:58:12

UI設(shè)計師按鈕

2015-07-17 08:27:19

EMMBYOD

2015-07-20 09:11:19

企業(yè)移動管理EMMBYOD安全

2011-12-27 16:31:27

程序員

2019-08-18 23:02:43

碼農(nóng)程序員開發(fā)

2023-03-07 07:05:29

生產(chǎn)數(shù)據(jù)庫運維

2021-07-29 10:37:13

漏洞管理自我修養(yǎng)漏洞

2015-10-28 13:39:25

2022-12-20 16:39:09

智能駕駛智駕產(chǎn)品

2014-05-12 10:02:56

數(shù)據(jù)可視化

2016-11-11 14:58:48

IBM 服務(wù)器

2017-10-20 17:21:16

華為

2020-10-20 14:01:16

HTTP

2020-09-18 10:12:24

KotlinTCP網(wǎng)絡(luò)協(xié)議

2016-10-11 11:38:06

程序員

2019-09-30 09:00:00

開發(fā)者技能工具

2019-01-14 15:11:13

TCP協(xié)議區(qū)塊鏈互聯(lián)網(wǎng)

2014-09-25 09:41:07

設(shè)計師

2013-02-21 16:44:13

系統(tǒng)工程師的sed

2023-04-04 12:24:10

點贊
收藏

51CTO技術(shù)棧公眾號