TCP與UDP挖掘機(jī)(一)初識

面試官偶爾會問到TCP相關(guān)的知識點(diǎn),在最早之前我是一臉懵逼答非所問的,故整理了一下關(guān)于TCP的相關(guān)知識點(diǎn),希望對大家有所收獲!

為了更進(jìn)一步了解網(wǎng)絡(luò)層面的知識,先曬出一張網(wǎng)絡(luò)體系結(jié)構(gòu)圖,加深理解

計算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)圖

all.png

TCP UDP的區(qū)別

TCP UDP
面向連接的協(xié)議?;谶@種連接方式, 通信設(shè)備應(yīng)在傳輸數(shù)據(jù)前建立連接,并應(yīng)在傳輸數(shù)據(jù)后關(guān)閉連接 面向數(shù)據(jù)報的協(xié)議。意味著打開、維護(hù)、終止連接不會有開銷。UDP對于廣播和多播類型的網(wǎng)絡(luò)傳輸是有效的
點(diǎn)對點(diǎn)通信,連接兩端的socket
面向字節(jié)流。TCP把傳輸?shù)母鞣N數(shù)據(jù)當(dāng)做無結(jié)構(gòu)的字節(jié)流來用
可靠性。它能夠保證向目標(biāo)路由器的數(shù)據(jù)傳輸 不可靠性。不能保證向目的地傳送數(shù)據(jù)
錯誤檢測機(jī)制。TCP提供了廣泛的錯誤檢查機(jī)制,這是因為它提供流量控制和數(shù)據(jù)確認(rèn)。 UDP只有使用校驗和的基本錯誤檢查機(jī)制
數(shù)據(jù)排序。數(shù)據(jù)包能夠按照順序到達(dá)接收器 沒有數(shù)據(jù)排序。若有需求,則需要再應(yīng)用程序?qū)舆M(jìn)行管理
速度較慢。相對UDP而言速度較慢。 快、簡單、高效
重傳機(jī)制。支持重傳丟失的數(shù)據(jù)包 無重傳機(jī)制
標(biāo)頭大小為20個字節(jié) 標(biāo)頭大小為8個字節(jié)
重量級 輕量級
用于HTTP,HTTP,F(xiàn)TP,SMTP和Telnet 用于DNS,DHCP,TFTP,SNMP,RIP和VoIP

注:本文所指的Client 均為發(fā)送方,Server為接收方

TCP 三次握手、四次揮手

三次握手

建立一個TCP連接時,需要Client和Server總共發(fā)送3個包。

三次握手的目的是連接服務(wù)器指定端口,建立 TCP 連接,并同步連接雙方的序列號和確認(rèn)號,交換 TCP 窗口大小信息。在 socket 編程中,客戶端執(zhí)行 connect() 時。將觸發(fā)三次握手。

3-way-handshake_1.jpg
  • Step 1(SYN).

    Client 端要和Server端 建立連接,所以要發(fā)一個SYN(即同步序列號)的包,初始序號x,保存在包頭的序列號(Sequence Number)字段里,指明打算連接的Service port,。

    用于告知Server:我(Client)可能要與你開始通訊了,現(xiàn)在發(fā)給你一個我(Client)啟動段的序列號。

    此時Client進(jìn)入SYN_SEND狀態(tài)

  • Step 2(SYN+ACK).
    Server 端 接收到數(shù)據(jù)包(通知)后

    使用一個SYN-ACK信號位設(shè)置,來響應(yīng)Client 端的請求。

    即發(fā)送了自己的序列號(SVN),初始序號為y,和確認(rèn)號(ACK,即Client發(fā)來的序列號遞增1, 即x + 1)。

    此時Server進(jìn)入 SYN_RCVD 狀態(tài)

  • Step 3(ACK).

    Client接收到Server端的響應(yīng)后

    發(fā)送確認(rèn)包(ACK,即Server 發(fā)來的序列號遞增1, 即y + 1)來確認(rèn)收到響應(yīng),此時Client 進(jìn)入 ESTABLISHED 狀態(tài),當(dāng)Server 接收到該ACK包后,也進(jìn)入 ESTABLISHED 狀態(tài)。

四次揮手

TCP 的連接的拆除需要發(fā)送四個包,因此稱為四次揮手(Four-way handshake)。

需要四個包的原因是是因為TCP的半關(guān)閉引起的

客戶端或服務(wù)器均可主動發(fā)起揮手動作,在 socket 編程中,任何一方執(zhí)行 close() 操作即可產(chǎn)生揮手操作。

下面假設(shè)Client主動發(fā)起揮手動作

4-way-connect-termination.jpg
  • Step 1(FIN).

    Client 端(發(fā)起方)要關(guān)閉TCP連接,所以要發(fā)一個FIN包,序號為x

    發(fā)送完畢后,此時Client進(jìn)入 FIN_WAIT_1 狀態(tài)(此時表明無數(shù)據(jù)可發(fā)送,但仍可接受數(shù)據(jù))

  • Step 2(ACK).

    當(dāng)Server 端 接收到FIN包后,立即向Client發(fā)送確認(rèn)包(即Client發(fā)來的FIN包的序號遞增1,x + 1)。

    發(fā)送完畢后,此時Server 進(jìn)入 CLOSE_WAIT 狀態(tài)(此時表明接收到了Client的關(guān)閉,但還沒做好“思想準(zhǔn)備“關(guān)閉連接)

    當(dāng)Client 端 接收到ACK包后,進(jìn)入 FIN_WAIT_2 狀態(tài)

  • Step 3(FIN).

    Server 端 發(fā)送ACK包一段時間(這段時間它有一些關(guān)閉過程)后,開始發(fā)送FIN包,序號為y

    發(fā)送完畢后,此時Server 進(jìn)入 LAST_ACK 狀態(tài)

  • Step 4(ACK).

    當(dāng)Client 端 接收到FIN包,即關(guān)閉請求后,發(fā)送一個確認(rèn)包(即Server發(fā)來的FIN包的序號遞增1,y + 1

    發(fā)送完畢后,此時Client 進(jìn)入 TIME_WAIT 狀態(tài),目的在于在時間周期n內(nèi),允許Client 在發(fā)送的ACK包丟失的情況下重新發(fā)

    當(dāng)Server 端接收到ACK包后,連接正式關(guān)閉,此時Server進(jìn)入 CLOSED 狀態(tài)。Client資源(包括端口號、緩沖區(qū)數(shù)據(jù))都被釋放

    當(dāng)Client在時間周期n結(jié)束后,仍沒收到Server 發(fā)的ACK包,則認(rèn)為已正常關(guān)閉連接,此時Client 也進(jìn)入 CLOSE 狀態(tài)

TCP協(xié)議如何保證可靠傳輸

從上面的體系圖可以看到,TCP(即運(yùn)輸層)的報文信息最終會交付到網(wǎng)際層。而網(wǎng)際層不會提供可靠的服務(wù)。所以還是要TCP來保證可靠的傳輸,才能最終保證數(shù)據(jù)服務(wù)的可靠

原理

? 宏觀上看,從TCP的特性可以得知,它有自己的錯誤檢測機(jī)制、數(shù)據(jù)按序傳輸、確認(rèn)應(yīng)答+序列號、支持重傳的功能。然后具體的內(nèi)部處理是怎樣的呢?主要有以下兩點(diǎn)

  • 停止等待協(xié)議

    這是最簡單的保證可靠傳輸?shù)膮f(xié)議

    以下會發(fā)生兩種情況

    • 無差錯

      停止等待協(xié)助-無差錯.jpg

    可以看到Client 在發(fā)送分組M1(即數(shù)據(jù)單元)后,暫停,等到Server發(fā)回確認(rèn)后,繼續(xù)發(fā)送下一個分組...

    這是理想條件下的無差錯情況

    • 有差錯

      停止等待協(xié)助-超時重傳.jpg

當(dāng)Client 發(fā)送 分組M1(會先設(shè)置一個計時器,在此計時器內(nèi)M1仍存在,以便重傳)時,可能會遇到數(shù)據(jù)無法到達(dá)Server,或者Server 檢測出問題并丟棄了它,在指定時間內(nèi)Client 如果未收到來自Server 的確認(rèn),則會重傳M1,即人們常說的超時重傳

超時重傳會有以下情況

  • 確認(rèn)丟失(發(fā)回延遲)

    Client發(fā)送分組M1,Server收到M1并發(fā)送確認(rèn)分組,而在指定的時間內(nèi)Client沒有收到確認(rèn),后會重傳M1.

    而由于Server 已經(jīng)收到過M1了, 所以此時它需要 丟棄M1分組, 發(fā)送確認(rèn)分組

  • 確認(rèn)遲到(發(fā)送延遲)

    由于網(wǎng)絡(luò)延遲等原因,Client發(fā)送的分組M1,在指定時間后才到Server, 此時Client 還沒來得及收到確認(rèn),再次發(fā)送分組M1

    而由于Server 剛好收到了M1,所以此時它需要 丟棄M1分組, 發(fā)送確認(rèn)分組,

    Client 收到>=2個以上的確認(rèn),會執(zhí)行丟棄操作,并且停止發(fā)送

  • 連續(xù)ARQ協(xié)議

    由于停止等待協(xié)議對信道的利用率太低,故可以采用流水線的方式來傳輸,即連續(xù)ARQ協(xié)議。

    這里需要提到一個發(fā)送窗口的概念。發(fā)送窗口支持滑動,所以也有滑動窗口這么一個概念

    ? Client 會維護(hù)一個發(fā)送窗口,一個窗口內(nèi)可以有多個連續(xù)分組進(jìn)行發(fā)送,而不必等待對方的確認(rèn)一條條分組發(fā)。

    ? Server 亦不會對每個分組進(jìn)行回傳確認(rèn),而是在按需發(fā)送到達(dá)的最后一個分組到達(dá)之后,發(fā)送確認(rèn),代表這個窗口的分組已經(jīng)發(fā)送成功

具體實(shí)現(xiàn)

1. 使用滑動窗口

? 窗口主要分為接收窗口和發(fā)送窗口

  • 接收窗口

    接收窗口.png

“接收窗口”大小取決于應(yīng)用(比如說tomcat:8080端口的監(jiān)聽進(jìn)程)、系統(tǒng)、硬件的限制。圖中,接收窗口是31~50,大小為20。

在接收窗口中,黑色的表示已收到的數(shù)據(jù),白色的表示未收到的數(shù)據(jù)。

當(dāng)收到窗口左邊的數(shù)據(jù),如27,則丟棄,因為這部分已經(jīng)交付給主機(jī);

當(dāng)收到窗口右邊的數(shù)據(jù),如52,則丟棄,因為還沒輪到它;

當(dāng)收到已收到的窗口中的數(shù)據(jù),如32,丟棄;

當(dāng)收到未收到的窗口中的數(shù)據(jù),如35,緩存在窗口中。

  • 發(fā)送窗口

?
發(fā)送窗口.png

? 發(fā)送窗口的大小swnd=min(rwnd,cwnd)。rwnd是接收窗口,cwnd用于擁塞控制,暫時可以理解swnd= rwnd =20。

圖中分為四個區(qū)段,其中P1到P3是發(fā)送窗口。

tips:發(fā)送窗口以字節(jié)為單位。為了方便畫圖,圖中展示得像以報文為單位一樣。但這不影響理解。

2. 重傳與確認(rèn)

  • 確認(rèn)

    這里主要是通過累計確認(rèn)的方式

  • 重傳

    這里主要是上面說的超時重傳,每一個報文都會有超時計數(shù)器,當(dāng)超過指定時間后,Client(發(fā)送方)會觸發(fā)重傳報文

3. 流量控制(基于滑動窗口)

? 流量即發(fā)送方發(fā)送的報文流量。當(dāng)接收方來不及處理數(shù)據(jù)時,通過滑動窗口,告訴發(fā)送方能夠接受的單位字節(jié)是多少,以降低發(fā)送的頻率,防止包丟失

流量控制.png
  • 在建立連接時,接收方(B),告訴了發(fā)送方(A):我的接收窗口是400(單位字節(jié)).

  • 圖中的ACK為TCP首部的ACK字段,ack為首部的確認(rèn)號字段.

  • 流量控制體現(xiàn)在:rwnd=300, rwnd=100, rwnd=0.在確認(rèn)報文的窗口字段設(shè)定了發(fā)送方能夠發(fā)出的數(shù)據(jù)多少,從而控制流量.注意只有到首部的ACK字段值為1,窗口字段的值才有效.

  • 假設(shè)在B發(fā)送了rwnd=0之后,過段時間由于自己又希望接收到數(shù)據(jù),于是發(fā)出rwnd=400的報文,但是該報文丟失了,這樣A依然無法發(fā)送數(shù)據(jù),B希望接收但接收不到數(shù)據(jù).

? 為解決該問題,TCP為每個鏈接都設(shè)有一個持續(xù)計時器.只要接收到對方窗口為0的通知,就啟動持續(xù)計時器.在計時器到期后,就發(fā)送探測報文,對方可以在該報文的確認(rèn)中告知當(dāng)前的窗口值.若窗口任然為0,那么就重新設(shè)定計時器,若不為0,那么上述的問題就解決了.

4. 擁塞控制

? 擁塞是指對網(wǎng)絡(luò)某一資源(帶寬,緩存等)的需求超過了可提供的部分,從而使網(wǎng)絡(luò)中傳送的數(shù)據(jù)不能按時到達(dá),網(wǎng)絡(luò)性能變差的情況.

擁塞控制就是防止過多的數(shù)據(jù)注入到網(wǎng)絡(luò)中,這樣網(wǎng)絡(luò)中的資源壓力就小了.

流量控制和擁塞控制似乎很相似,但是他們不同.前者立足于接收和發(fā)送者雙方的情況;而后者注重的是數(shù)據(jù)量對網(wǎng)絡(luò)環(huán)境的影響

TCP 粘包、拆包

由于TCP 是一個面向字節(jié)流的協(xié)議,這也決定了它的數(shù)據(jù)是無結(jié)構(gòu)的。所以TCP無法得知應(yīng)用層對于這快數(shù)據(jù)的定義,而是基于自身緩沖區(qū)的實(shí)際情況進(jìn)行數(shù)據(jù)包的拆分,或者將多個數(shù)據(jù)包進(jìn)行合并來發(fā)送。

參考下圖,在不同的條件下,會發(fā)生多種現(xiàn)象

  • Server 分別接收P1,P2,沒有發(fā)生粘包、拆包
  • Server 一次接收P1+P2兩個報文,發(fā)生了粘包
  • Server 先接收P2, 再分別接收了P1_1, P1_2,發(fā)生了拆包
  • Server 先接收了P2+P1_2,再接收了P1_1,發(fā)生了粘包、拆包
  • 另一種極端情況,當(dāng)窗口非常小,恰逢P(guān)1又很大時,可能會發(fā)生多次對P1進(jìn)行拆包
粘包與拆包.jpg

首先我們要知道,發(fā)送的數(shù)據(jù)會先傳入發(fā)送緩沖區(qū),再通過網(wǎng)絡(luò)傳輸發(fā)送到接收端的緩沖區(qū)

以上現(xiàn)象發(fā)生的原因主要是

  • 發(fā)送的字節(jié) 大于 TCP發(fā)送緩沖區(qū)的大小,會發(fā)生拆包
  • 發(fā)送的報文 大于 MSS(最大報文長度),會發(fā)生拆包
  • 發(fā)送的字節(jié) 小于 TCP發(fā)送緩沖區(qū)的大小,會將多次寫入緩沖區(qū)的報文一并發(fā)送,即發(fā)生粘包

解決方案,需要上層應(yīng)用程序做對應(yīng)的處理

  • 規(guī)定報文長度。例如設(shè)定每條報文固定長度為200字節(jié),當(dāng)不夠時,用空格填充
  • 報文末尾添加回車換行符。例如FTP協(xié)議
  • 將報文分為header and body,在頭部中聲明報文長度,然后根據(jù)這個長度來獲取報文

我們常用的Netty 已經(jīng)幫我們處理好這些問題,我們僅需調(diào)用特定的方法即可。這個在后續(xù)的Netty挖掘機(jī)系列文章會提到栗子。

比如有:

  • LineBasedFrameDecoder 基于換行符解決
  • DelimiterBasedFrameDecoder 基于分隔符解決
  • FixedLengthFrameDecoder 指定長度解決

參考鏈接:【讀】這一次,讓我們再深入一點(diǎn) - TCP協(xié)議

參考鏈接:什么是 TCP 拆、粘包?如何解決?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 如果對網(wǎng)絡(luò)工程基礎(chǔ)不牢,建議通讀《細(xì)說OSI七層協(xié)議模型及OSI參考模型中的數(shù)據(jù)封裝過程?》 下面就是TCP/IP...
    zhoulujun閱讀 3,266評論 1 10
  • 運(yùn)輸層協(xié)議概述 從通信和信息處理的角度看,運(yùn)輸層向它上面的應(yīng)用層提供通信服務(wù),它屬于面向通信部分的最高層,同時也是...
    srtianxia閱讀 2,444評論 0 2
  • 端口與進(jìn)程 TCP 的包是不包含 IP 地址信息的,那是 IP 層上的事,但是有源端口和目的端口。 就是說,端口這...
    XLsn0w閱讀 761評論 2 4
  • 個人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,090評論 0 8
  • 前幾天,一個朋友跟我說:你的背影好滄桑。我當(dāng)時不以為然,現(xiàn)在感觸頗深,回想起來好像有那么一絲對與不對的見解。對:月...
    陶仕冶閱讀 235評論 2 3