這一篇TCP總結請收下

前言

很高興碰見你~android

TCP這些東西,基本每一個程序猿都或多或少是掌握的了。雖然感受在實際開發中沒有什麼用武之處,但,面試他要問啊git

而最近你們夥過完年,也都在準備春招,我也同樣。閱讀了一些okHttp源碼以後,又屁顛屁顛地跑回來從新把tcp、http這些東西給從新學了一遍。okHttp基本都是這些協議的實現,而理解源碼的基礎是,理解tcp、http。web

從新看了一遍tcp以後,我把這些東西給總結了下來,也就有了這篇文章。面試

計算機網絡的知識特色就是:瑣碎。靠背誦「面試八股文」估計沒多久就忘了。TCP是計算機網絡運輸層的一個協議,因此首先要對計網分層結構以及運輸層有必定的理解。而後是TCP的四個重點:面向鏈接、可靠傳輸原理、流量控制和擁塞控制,最後再補充一點粘包和拆包的知識。算法

計網分層結構

考慮最簡單的狀況:兩臺主機之間的通訊。這個時候只須要一條網線把二者連起來,規定好彼此的硬件接口,如都用USB、電壓10v、頻率2.4GHz等,這一層就是物理層,這些規定就是物理層協議設計模式


咱們固然不知足於只有兩臺電腦鏈接,所以咱們可使用交換機把多個電腦鏈接起來,以下圖:緩存

image.png

這樣鏈接起來的網絡,稱爲局域網,也能夠稱爲以太網(以太網是局域網的一種)。在這個網絡中,咱們須要標識每一個機器,這樣才能夠指定要和哪一個機器通訊。這個標識就是硬件地址MAC。硬件地址隨機器的生產就被肯定,永久性惟一。在局域網中,咱們須要和另外的機器通訊時,只須要知道他的硬件地址,交換機就會把咱們的消息發送到對應的機器。服務器

這裏咱們能夠無論底層的網線接口如何發送,把物理層抽離,在他之上建立一個新的層次,這就是數據鏈路層微信


咱們依然不知足於局域網的規模,須要把全部的局域網聯繫起來,這個時候就須要用到路由器來鏈接兩個局域網:網絡

image.png

可是若是咱們仍是使用硬件地址來做爲通訊對象的惟一標識,那麼當網絡規模愈來愈大,須要記住全部機器的硬件地址是不現實的;同時,一個網絡對象可能會頻繁更換設備,這個時候硬件地址表維護起來更加複雜。這裏使用了一個新的地址來標記一個網絡對象:IP地址

經過一個簡單的寄信例子來理解IP地址。

我住在北京市,我朋友A住在上海市,我要給朋友A寫信:

  1. 寫完信,我會在信上寫好我朋友A的地址,並放到北京市郵局(給信息附加目標IP地址,併發送給路由器)
  2. 郵局會幫我把信運輸到上海市當地郵局(信息會通過路由傳遞到目標IP局域網的路由器)
  3. 上海市當地路由器會幫我把信交給朋友A(局域網內通訊)

所以,這裏IP地址就是一個網絡接入地址(朋友A的住址),我只須要知道目標IP地址,路由器就能夠把消息給我帶到。在局域網中,就能夠動態維護一個MAC地址與IP地址的映射關係,根據目的IP地址就能夠尋找到機器的MAC地址進行發送

這樣咱們不需管理底層如何去選擇機器,咱們只須要知道IP地址,就能夠和咱們的目標進行通訊。這一層就是網絡層。網絡層的核心做用就是 提供主機之間的邏輯通訊 。這樣,在網絡中的全部主機,在邏輯上都鏈接起來了,上層只須要提供目標IP地址和數據,網絡層就能夠把消息發送到對應的主機。


一個主機有多個進程,進程之間進行不一樣的網絡通訊,如邊和朋友開黑邊和女友聊微信。個人手機同時和兩個不一樣機器進行通訊。那麼當個人手機收到數據時,如何區分是微信的數據,仍是王者的數據?那麼就必須在網絡層之上再添加一層:運輸層

運輸層經過socket(套接字),將網絡信息進行進一步的拆分,不一樣的應用進程能夠獨立進行網絡請求,互不干擾。這就是運輸層的最本質特色:提供進程之間的邏輯通訊 。這裏的進程能夠是主機之間,也能夠是同個主機,因此在android中,socket通訊也是進程通訊的一種方式。


如今不一樣的機器上的應用進程之間能夠獨立通訊了,那麼咱們就能夠在計算機網絡上開發出形形式式的應用:如web網頁的http,文件傳輸ftp等等。這一層稱爲應用層

應用層還能夠進一步拆分出表示層、會話層,但他們的本質特色都沒有改變:完成具體的業務需求 。和下面的四層相比,他們並非必須的,能夠歸屬到應用層中。


最後對計網分層進行小結:

image.png

  1. 最底層物理層,負責兩個機器之間經過硬件的直接通訊;
  2. 數據鏈路層使用硬件地址在局域網中進行尋址,實現局域網通訊;
  3. 網絡層經過抽象IP地址實現主機之間的邏輯通訊;
  4. 運輸層在網絡層的基礎上,對數據進行拆分,實現應用進程的獨立網絡通訊;
  5. 應用層在運輸層的基礎上,根據具體的需求開發形形式式的功能。

這裏須要注意的是,分層並非在物理上的分層,而是邏輯上的分層。經過對底層邏輯的封裝,使得上層的開發能夠直接依賴底層的功能而無需理會具體的實現,簡便了開發。

這種分層的思路,也就是責任鏈設計模式,經過層層封裝,把不一樣的職責獨立起來,更加方便開發、維護等等。okHttp中的攔截器設計模式,也是這種責任鏈模式。

運輸層

本文主要是講解TCP,這裏須要增長一些運輸層的知識。

本質:提供進程通訊

yqs3gx.md.png

在運輸層之下的網絡層,是不知道該數據包屬於哪一個進程,他只負責數據包的接收與發送。運輸層則負責接收不一樣進程的數據交給網絡層,同時把網絡層的數據拆分交給不一樣的進程。從上往下匯聚到網絡層,稱爲多路複用,從下往上拆分,稱爲多路拆分

運輸層的表現,受網絡層的限制。這很好理解,網絡層是運輸層的底層支持。因此運輸層是沒法決定本身帶寬、時延等的上限。但能夠基於網絡層開發更多的特性:如可靠傳輸。網絡層只負責盡力把數據包從一端發送到另外一端,而不保證數據能夠到達且完整。

底層實現:socket

前面講到,最簡單的運輸層協議,就是提供進程之間的獨立通訊 ,但底層的實現,是socket之間的獨立通訊 。在網絡層中,IP地址是一個主機邏輯地址,而在運輸層中,socket是一個進程的邏輯地址;固然,一個進程能夠擁有多個socket。應用進程能夠經過監聽socket,來獲取這個socket接受到的消息。

舉個例子來理解socket。以下圖

每個主機能夠建立不少個socket來接收信息。如主機A的微信進程,想要發送給主機B的微信,那麼他只須要發送給主機B的socketC,主機B的微信就會從socketC中取到消息。(固然實際的流程不是這樣的,咱們的消息須要通過微信後臺服務器,這裏只是舉例子)

同理,主機B的QQ,想要發送消息給主機A的QQ,那麼只須要把消息發送給socketB,主機A的QQ就能夠拿到消息了。

socket並非一個實實在在的東西,而是運輸層抽象出來的一個對象。運輸層增長了端口這個概念,來區分不一樣的socket。端口能夠理解爲一個主機上有不少的網絡通訊口,每一個端口都有一個端口號,端口的數量由運輸層協議肯定。

不一樣的運輸層協議對socket有不一樣的定義方式。在UDP協議中,使用目標IP+目標端口號來定義一個socket;在TCP中使用目標IP+目標端口號+源IP+源端口號來定義一個socket。咱們只須要在運輸層報文的頭部附加上這些信息,目標主機就會知道咱們要發送給哪一個socket,對應監聽該socket的進程就可得到信息。

運輸層協議

運輸層的協議就是大名鼎鼎的TCP和UDP。其中,UDP是最精簡的運輸層協議,只實現了進程間的通訊;而TCP在UDP的基礎上,實現了可靠傳輸、流量控制、擁塞控制、面向鏈接等等特性,同時也更加複雜。

固然除此以外,還有更多更優秀的運輸層協議,但目前廣爲使用的,就是TCP和UDP。UDP在後面也會總結到。

TCP協議首部

TCP協議,表如今報文上,就是會在應用層傳輸下來的數據前附加上一個TCP首部,這個首部附加了TCP信息,先來總體看一下這個首部的結構:

這張圖是來自我大學老師的課件, 很是好用,因此一直拿來學習。最下面部分表示了報文之間的關係,TCP數據部分就是應用層傳下來的數據。

TCP首部固定長度是20字節,下面還有4字節是可選的。內容不少,但其中有一些咱們比較熟悉的:源端口,目標端口。嗯?socket不是還須要IP進行定位嗎?IP地址在網絡層被附加了。其餘的內容後面都會慢慢講解,做爲一篇總結文章,這裏放出查閱表,方便複習:

頭部參數 字節數 做用
源端口和目的端口字段 各佔兩字節 socket是經過端口號和IP號來進行定義,這裏表示發出消息的主機端口以及接收消息的目標主機端口
序號字段 4 字節 TCP 鏈接中傳送的數據流中的每個字節都編上一個序號。序號字段的值則指的是本報文段所發送的數據的第一個字節的序號。長度4字節,因此序號的範圍是【0,2^32 - 1】
確認號字段 4字節 是指望收到對方的下一個報文段的數據的第一個字節的序號。
數據偏移(即首部長度) 4位 指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。「數據偏移」的單位是 32 位(以 4 字節爲計算單位)
保留字段 6位 保留爲從此使用,但目前應置爲 0
緊急 URG 1位 當 URG =1 時,代表緊急指針字段有效。它告訴系統此報文段中有緊急數據,應儘快傳送(至關於高優先級的數據)
確認 ACK 1位 只有當 ACK=1 時確認號字段纔有效。當 ACK = 0 時,確認號無效。當收到報文須要向發送方發送確認報時設置該標誌位爲1。
推送 PSH 1位 接收 TCP 收到 PSH = 1 的報文段,就儘快地交付接收應用進程,而再也不等到整個緩存都填滿了後再向上交付。
復位 RST 1位 當 RST =1 時,代表 TCP 鏈接中出現嚴重差錯(如因爲主機崩潰或其餘緣由),必須釋放鏈接,而後再從新創建運輸鏈接。
同步 SYN 1位 同步 SYN = 1 表示這是一個鏈接請求或鏈接接受報文。
終止 FIN 1位 用來釋放一個鏈接。FIN = 1 代表此報文段的發送端的數據已發送完畢,並要求釋放運輸鏈接
窗口字段 2字節 發送方接收緩存區剩下的字節 數,注意單位是字節。
檢驗和 2字節 檢驗和字段檢驗的範圍包括首部和數據這兩部分。在計算檢驗和時,要在 TCP 報文段的前面加上 12 字節的僞首部。主要是檢驗報文是否發生了錯誤,如某個‘1’變成了‘0’。
緊急指針字段 2字節 指出在本報文段中緊急數據共有多少個字節(緊急數據放在本報文段數據的最前面)
選項字段 長度不定 TCP 最初只規定了一種選項,即最大報文段長度 MSS。MSS 告訴對方 TCP:「個人緩存所能接收的報文段的數據字段的最大長度是 MSS 個字節。」
填充字段 不定 這是爲了使整個首部長度是 4 字節的整數倍。

選項字段中包含如下其餘選項:

選項 做用
窗口擴大選項 佔 3 字節,其中有一個字節表示移位值 S。新的窗口值等於 TCP 首部中的窗口位數增大到 (16 + S),至關於把窗口值向左移動 S 位後得到實際的窗口大小
時間戳選項 佔 10 字節,其中最主要的字段時間戳值字段(4 字節)和時間戳回送回答字段(4 字節),主要是用於計算數據報在網絡中傳輸的往返時間。
選擇確認選項 接收方收到了和前面的字節流不連續的兩個字節塊,須要告訴發送方目前已經接收到的數據報範圍。每個段須要兩個邊界,一個邊界須要4字節來表示,選項字段最長是40字節,因此最多能夠表示4個已接收的字段。

講完下面內容,再回來看這些字段就熟悉了。

TCP面向字節流特性

TCP並非把應用層傳輸過來的數據直接加上首部而後發送給目標,而是把數據當作一個字節 流,給他們標上序號以後分部分發送。這就是TCP的 面向字節流 特性:

  • TCP會以流的形式從應用層讀取數據並存放在本身的發送緩存區中,同時爲這些字節標上序號
  • TCP會從發送方緩衝區選擇適量的字節組成TCP報文,經過網絡層發送給目標
  • 目標會讀取字節並存放在本身的接收方緩衝區中,並在合適的時候交付給應用層

面向字節流的好處是無需一次存儲過大的數據佔用太多內存,壞處是沒法知道這些字節表明的意義,例如應用層發送一個音頻文件和一個文本文件,對於TCP來講就是一串字節流,沒有意義可言,這會致使粘包以及拆包問題,後面講。

可靠傳輸原理

前面講到,TCP是可靠傳輸協議,也就是,一個數據交給他,他確定能夠完整無誤地發送到目標地址,除非網絡炸了。他實現的網絡模型以下:

對於應用層來講,他就是一個可靠傳輸的底層支持服務;而運輸層底層採用了網絡層的不可靠傳輸。雖然在網絡層甚至數據鏈路層就可使用協議來保證數據傳輸的可靠性,但這樣網絡的設計會更加複雜、效率會隨之下降。把數據傳輸的可靠性保證放在運輸層,會更加合適。

可靠傳輸原理的重點總結一下有:滑動窗口、超時重傳、累積確認、選擇確認、連續ARQ

中止等待協議

要實現可靠傳輸,最簡便的方法就是:我發送一個數據包給你,而後你跟我回復收到,我繼續發送下一個數據包。傳輸模型以下:

這種「一來一去」的方法來保證傳輸可靠就是中止等待協議(stop-and-wait)。不知道還記不記得前面TCP首部有一個ack字段,當他設置爲1的時候,表示這個報文是一個確認收到報文。

而後再來考慮一種狀況:丟包。網絡環境不可靠,致使每一次發送的數據包可能會丟失,若是機器A發送了數據包丟失了,那麼機器B永遠接收不到數據,機器A永遠在等待。解決這個問題的方法是:超時重傳 。當機器A發出一個數據包時便開始計時,時間到還沒收到確認回覆,就能夠認爲是發生了丟包,便再次發送,也就是重傳。

但重傳會致使另外一種問題:若是原先的數據包並無丟失,只是在網絡中待的時間比較久,這個時候機器B會受到兩個數據包,那麼機器B是如何辨別這兩個數據包是屬於同一份數據仍是不一樣的數據?這就須要前面講過的方法:給數據字節進行編號。這樣接收方就能夠根據數據的字節編號,得出這些數據是接下來的數據,仍是重傳的數據。

在TCP首部有兩個字段:序號和確認號,他們表示發送方數據第一個字節的編號,和接收方期待的下一份數據的第一個字節的編號。前面講到TCP是面向字節流,可是他並非一個字節一個字節地發送,而是一次截取一整段。截取的長度受多種因素影響,如緩存區的數據大小、數據鏈路層限制的幀大小等。

連續ARQ協議

中止等待協議已經能夠知足可靠傳輸了,但有一個致命缺點:效率過低。發送方發送一個數據包以後便進入等待,這個期間並無幹任何事,浪費了資源。解決的方法是:連續發送數據包。模型以下:

和中止等待最大的不一樣就是,他會源源不斷地發送,接收方源源不斷收到數據以後,逐一進行確認回覆。這樣便極大地提升了效率。但一樣,帶來了一些額外的問題:

發送是否能夠無限發送直到把緩衝區全部數據發送完?不能夠。由於須要考慮接收方緩衝區以及讀取數據的能力。若是發送太快致使接收方沒法接受,那麼只是會頻繁進行重傳,浪費了網絡資源。因此發送方發送數據的範圍,須要考慮到接收方緩衝區的狀況。這就是TCP的流量控制 。解決方法是:滑動窗口 。基本模型以下:

  • 發送方須要根據接收方的緩衝區大小,設置本身的可發送窗口大小,處於窗口內的數據表示可發送,以外的數據不可發送。
  • 當窗口內的數據接收到確認回覆時,整個窗口會往前移動,直到發送完成全部的數據

在TCP的首部有一個窗口大小字段,他表示接收方的剩餘緩衝區大小,讓發送方能夠調整本身的發送窗口大小。經過滑動窗口,就能夠實現TCP的流量控制,不至於發送太快,致使太多的數據丟失。


連續ARQ帶來的第二個問題是:網絡中充斥着和發送數據包同樣數據量的確認回覆報文,由於每個發送數據包,必須得有一個確認回覆。提升網絡效率的方法是:累積確認 。接收方不須要逐個進行回覆,而是累積到必定量的數據包以後,告訴發送方,在此數據包以前的數據全都收到。例如,收到 1234,接收方只須要告訴發送方我收到4了,那麼發送方就知道1234都收到了。

第三個問題是:如何處理丟包狀況。在中止等待協議中很簡單,直接一個超時重傳就解決了。但,連續ARQ中不太同樣。例如:接收方收到了 123 567,六個字節,編號爲4的字節丟失了。按照累積確認的思路,只能發送3的確認回覆,567都必須丟掉,由於發送方會進行重傳。這就是GBN(go-back-n) 思路。

可是咱們會發現,只須要重傳4便可,這樣不是很浪費資源,因此就有了:選擇確認SACK 。在TCP報文的選項字段,能夠設置已經收到的報文段,每個報文段須要兩個邊界來進行肯定。這樣發送方,就能夠根據這個選項字段只重傳丟失的數據了。

可靠傳輸小結

到這裏關於TCP的可靠傳輸原理就已經介紹的差很少。最後進行一個小結:

  • 經過連續ARQ協議與發送-確認回覆模式來保證每個數據包都到達接收方
  • 經過給字節編號的方法,來標記每個數據是屬於重傳仍是新的數據
  • 經過超時重傳的方式,來解決數據包在網絡中丟失的問題
  • 經過滑動窗口來實現流量控制
  • 經過累積確認+選擇確認的方法來提升確認回覆與重傳的效率

固然,這只是可靠傳輸的冰山一角,感興趣能夠再深刻去研究(和麪試官聊天已經差很少了[狗頭])。

擁塞控制

擁塞控制考慮的是另一個問題:避免網絡過度擁擠致使丟包嚴重,網絡效率下降

拿現實的交通舉例子:

高速公路同一時間可通行的汽車數量是必定的,當節假日時,就會發生嚴重的堵車。在TCP中,數據包超時,會進行重傳,也就是會進來更多的汽車,這時候更堵,最後致使的結果就是:丟包-重傳-丟包-重傳。最後整個網絡癱瘓了。

這裏的擁塞控制和前面的流量控制不是一個東西,流量控制是擁塞控制的手段:爲了不擁塞,必須對流量進行控制。擁塞控制目的是:限制每一個主機的發送的數據量,避免網絡擁塞效率降低。就像廣州等地,限制車牌號出行是一個道理。否則你們都堵在路上,誰都別想走。

擁塞控制的解決方法是流量控制,流量控制的實現是滑動窗口,因此擁塞控制最終也是經過限制發送方的滑動窗口大小來限制流量 。固然,擁塞控制的手段不僅是流量控制,致使擁塞的因素有:路由器緩存、帶寬、處理器處理速度等等。提高硬件能力(把4車道改爲8車道)是其中一個方法,但畢竟硬件提高是有瓶頸的,沒辦法不斷提高,仍是須要從tcp自己來增長算法,解決擁塞。

擁塞控制的重點有4個:慢開始、快恢復、快重傳、擁塞避免。這裏依舊獻祭出大學老師的ppt圖片:

Y軸表示的是發送方窗口大小,X軸表示的是發送的輪次(不是字節編號)。

  • 最開始的時候,會把窗口設置一個較小的值,而後每輪變爲原來的兩倍。這是慢開始。
  • 當窗口值到達ssthresh值,這個值是須要經過實時網絡狀況設置的一個窗口限制值,開始進入擁塞避免,每輪把窗口值提高1,慢慢試探網絡的底線。
  • 若是發生了數據超時,表示很可能發生了擁塞,而後回到慢開始,重複上面的步驟。
  • 若是收到三個相同的確認回覆,表示如今網絡的狀況不太好,把ssthresh的值設置爲原來的一半,繼續擁塞避免。這部分稱爲快恢復。
  • 若是收到丟包信息,應該儘快把丟失的包重傳一次,這是快重傳。
  • 固然,窗口的最終上限是不能無限上漲的,他不能超過接收方的緩存區大小。

經過這個算法,就能夠在很大程度上,避免網絡擁擠。

除此以外,還可讓路由器在緩存即將滿的時候,告知發送方我快滿了,而不是等到出現了超時再進行處理,這是主動隊列管理AQM。此外還有不少方法,可是上面的算法是重點。

面向鏈接

這一小節講的就是無人不曉的TCP三次握手與四次揮手這些,通過前面的內容,這一小節其實已經很好理解。

TCP是面向鏈接的,那鏈接是什麼?這裏的鏈接並非實實在在的鏈接,而是通訊雙方彼此之間的一個記錄 。TCP是一個全雙工通訊,也就是能夠互相發送數據,因此雙方都須要記錄對方的信息。根據前面的可靠傳輸原理,TCP通訊雙方須要爲對方準備一個接收緩衝區能夠接收對方的數據、記住對方的socket知道怎麼發送數據、記住對方的緩衝區來調整本身的窗口大小等等,這些記錄,就是一個鏈接。

在運輸層小節中講到,運輸層雙方通訊的地址是採用socket來定義的,TCP也不例外。TCP的每個鏈接只能有兩個對象,也就是兩個socket,而不能有三個。因此socket的定義須要源IP、源端口號、目標IP、目標端口號四個關鍵因素,纔不會發生混亂。

假如TCP和UDP同樣只採用目標IP+目標端口號來定義socket,那麼就會出現多個發送方同時發送到同一個目標socket的狀況。這個時候TCP沒法區分這些數據是否來自不一樣的發送方,就會致使出現錯誤。

既然是鏈接,就有兩個關鍵要點:創建鏈接、斷開鏈接。

創建鏈接

創建鏈接的目的就是交換彼此的信息,而後記住對方的信息。因此雙方都須要發送彼此的信息給對方:

但前面的可靠傳輸原理告訴咱們,數據在網絡中傳輸是不可靠的,須要對方給予咱們一個確認回覆,才能夠保證消息正確到達。以下圖:

機器B的確認收到和機器B信息能夠進行合併,減小次數;並且發送機器B給機器A自己就表明了機器B已經收到了消息,因此最後的示例圖是:

步驟以下:

  1. 機器A發送syn包向機器B請求創建TCP鏈接,並附加上自身的接收緩衝區信息等,機器A進入SYN_SEND狀態,表示請求已經發送正在等待回覆;
  2. 機器B收到請求以後,根據機器A的信息記錄下來,並建立自身的接收緩存區,向機器A發送syn+ack的合成包,同時自身進入SYN_RECV狀態,表示已經準備好了,等待機器A 的回覆就能夠向A發送數據;
  3. 機器A收到回覆以後記錄機器B 的信息,發送ack信息,自身進入ESTABLISHED狀態,表示已經徹底準備好了,能夠進行發送和接收;
  4. 機器B收到ACK數據以後,進入ESTABLISHED狀態。

三次消息的發送,稱爲三次握手。

斷開鏈接

斷開鏈接和三次握手相似,直接上圖:

  1. 機器A發送完數據以後,向機器B請求斷開鏈接,自身進入FIN_WAIT_1狀態,表示數據發送完成且已經發送FIN包(FIN標誌位爲1);

  2. 機器B收到FIN包以後,回覆ack包表示已經收到,但此時機器B可能還有數據沒發送完成,自身進入CLOSE_WAIT狀態,表示對方已發送完成且請求關閉鏈接,自身發送完成以後能夠關閉鏈接;

  3. 機器B數據發送完成以後,發送FIN包給機器B ,自身進入LAST_ACK狀態,表示等待一個ACK包便可關閉鏈接;

  4. 機器A收到FIN包以後,知道機器B也發送完成了,回覆一個ACK包,並進入TIME_WAIT狀態

    TIME_WAIT狀態比較特殊。當機器A收到機器B的FIN包時,理想狀態下,確實是能夠直接關閉鏈接了;可是:

    1. 咱們知道網絡是不穩定的,可能機器B 發送了一些數據還沒到達(比FIN包慢);
    2. 同時回覆的ACK包可能丟失了,機器B會重傳FIN包;

    若是此時機器A立刻關閉鏈接,會致使數據不完整、機器B沒法釋放鏈接等問題。因此此時機器A須要等待2個報文生存最大時長,確保網絡中沒有任何遺留報文了,再關閉鏈接

  5. 最後,機器A等待兩個報文存活最大時長以後,機器B 接收到ACK報文以後,均關閉鏈接,進入CLASED狀態

雙方之間4次互相發送報文來斷開鏈接的過程,就是四次揮手

如今,對於爲何握手是三次揮手是四次、必定要三次/四次嗎、爲何要停留2msl再關閉鏈接等等這些問題,就都解決了。

UDP協議

運輸層協議除了TCP,還有大名鼎鼎的UDP。若是說TCP憑藉他完善穩定的功能獨樹一幟,那UDP就是精簡主義亂拳打死老師傅。

UDP只實現了運輸層最少的功能:進程間通訊。對於應用層傳下來的數據,UDP只是附加一個首部就直接交給網絡層了。UDP的頭部很是簡單,只有三部分:

  • 源端口、目標端口:端口號用來區分主機的不一樣進程
  • 校驗碼:用於校驗數據包在傳輸的過程當中沒有出現錯誤,例如某個1變成了0
  • 長度:報文的長度

因此UDP的功能也只有兩個:校驗數據報是否發生錯誤、區分不一樣的進程通訊。

但,TCP的功能雖然多,但同時也是要付出相對應的代價。例如面向鏈接的特性,在創建和斷開鏈接的時候會有開銷;擁塞控制的特性,會限制傳輸的上限等等。下面來羅列一下UDP的優缺點:

UDP的缺點

  • 沒法保證消息完整、正確到達,UDP是一個不可靠的傳輸協議;
  • 缺乏擁塞控制容易互相競爭資源致使網絡系統癱瘓

UDP的優勢

  • 效率更快;不須要創建鏈接以及擁塞控制
  • 鏈接更多的客戶;沒有鏈接狀態,不須要爲每一個客戶建立緩存等
  • 分組首部字節少,開銷小;TCP首部固定首部是20字節,而UDP只有8字節;更小的首部意味着更大比例的數據部分
  • 在一些須要高效率容許可限度偏差的場景下可使用。如直播場景,並不須要保證每一個數據包都完整到達,容許必定的丟包率,這個時候TCP的可靠特性反而成爲了累贅;精簡的UDP更高的效率是更加適合的選擇
  • 能夠進行廣播;UDP並非面向鏈接的,因此能夠同時對多個進程進行發送報文

UDP適用場景

UDP適用於對傳輸模型須要應用層高度自定義、容許出現丟包、須要高效率的場景、須要廣播;例如

  • 視屏直播
  • DNS
  • RIP路由選擇協議

其餘補充

分塊傳輸

咱們能夠發現,運輸層在傳輸數據的時候,並非把整個數據包加個首部直接發送過去,而是會拆分紅多個報文分開發送;那他這樣作緣由是什麼?

有讀者可能會想到:數據鏈路層限制了數據長度只能有1460。那數據鏈路層爲何要這麼限制?他的本質緣由就是:網絡是不穩定的。若是報文太長,那麼極有可能在傳輸通常的時候忽然中斷了,這個時候就要整個數據重傳,效率就下降了。把數據拆分紅多個數據報,那麼當某個數據報丟失,只須要重傳該數據報便可。

那是否是拆分得越細越好?報文中數據字段長度過低,會使得首部的佔比太大,這樣首部就會成爲網絡傳輸最大的負擔了。例如1000字節,每一個報文首部是40字節,若是拆分紅10個報文,那麼只須要傳輸400字節的首部;而若是拆分紅1000個,那麼須要傳輸40000字節的首部,效率就極大地下降了。

路由轉換

先看下圖:

  • 正常狀況下,主機A的數據包能夠又 1-3-6-7路徑進行傳送
  • 若是路由3壞掉了,那麼能夠從 1-4-6-7進行傳送
  • 若是4也壞掉了,那麼只能從2-5-6-7傳送
  • 若是5壞掉了,那麼就中斷線路了

能夠看出來,使用路由轉發的好處是:提升網絡的容錯率,本質緣由依舊是網絡是不穩定的 。即便壞掉幾個路由器,網絡依舊暢通。可是若是壞掉路由器6那就直接致使主機A和主機B沒法通訊,因此要避免這種核心路由器的存在。

使用路由的好處還有:分流。若是一條線路太擁堵,能夠從別的路線進行傳輸,提升效率。

粘包與拆包

在面向字節流那一小節講過,TCP不懂這些數據流的意義,他只知道從應用層拿到數據流,切割成一份份報文,而後發送給目標對象。而若是應用層傳輸下來的是兩個數據包,那麼極有可能出現這種狀況:

  • 應用層須要向目標進程發送兩份數據,一份音頻,一份文本
  • TCP只知道接收到一個流,並把流拆分紅4段進行發送
  • 中間第二個報文的數據就出現兩個文件的數據混在一塊兒,這就是粘包
  • 目標進程應用層在接收到數據以後,須要把這些數據拆分紅正確的兩個文件,就是拆包

粘包與拆包都是應用層須要解決的問題,能夠在每一個文件的最後附加上一些特殊的字節,如換行符;或者控制每一個報文只包含一個文件的數據,不足的用0補充等等。

惡意攻擊

TCP的面向鏈接特色可能會被惡意的人利用,對服務器進行攻擊。

前面咱們知道,當咱們向一個主機發送syn包請求建立鏈接時,服務器會爲咱們建立緩衝區等,而後向咱們返回syn+ack報文;若是咱們僞造IP和端口,向一個服務器進行海量的請求,會使得服務器建立了大量的建立一半的TCP鏈接,使得其沒法正常響應用戶的請求,致使服務器癱瘓。

解決的方法能夠有限制IP的建立鏈接數、讓建立一半的tcp鏈接在更短的時間內自行關閉、延緩接收緩衝區內存的分配等等。

長鏈接

咱們向服務器的每一次請求都須要建立一個TCP鏈接,服務器返回數據以後就會關閉鏈接;若是在短期內有大量的請求,那麼頻繁建立TCP鏈接關閉TCP鏈接是一個很浪費資源的行爲。因此咱們可讓TCP鏈接不要關閉,在這個期間進行請求,提升效率。

須要注意長鏈接維持時間、建立條件等,避免被惡意利用建立大量的長鏈接,消耗殆盡服務器的資源。

最後

之前學習的時候以爲這些東西好像沒什麼卵用,貌似就是用來考試的。事實上,在沒應用到的時候,對這些知識很難有更深層次的認知,例如如今我看上面的總結,不少只是表面上的認知,不知道他背後表明的真正含義。

但當我學習的更加普遍、深刻,會對這些知識有愈來愈深入的認識。有那麼幾個瞬間以爲:哦原來那個東西是這樣運用,那個東西是這樣的啊,原來學了是真的有用。

如今可能學了以後沒有什麼感受,可是當用到或者學到相關的應用時,會有一個頓悟感,會瞬間收穫不少。

以爲有幫助留個贊鼓勵一下做者吧~

全文到此,原創不易,以爲有幫助能夠點贊收藏評論轉發。
筆者才疏學淺,文章有錯誤或有不一樣觀點歡迎評論區交流。
如需轉載請評論區或私信告知便可。

另外歡迎光臨筆者的我的博客:傳送門

相關文章
相關標籤/搜索