日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

网络编程(五) ———— 万字详解TCP协议

發(fā)布時(shí)間:2023/12/18 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络编程(五) ———— 万字详解TCP协议 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • TCP首部格式
  • 1.確認(rèn)應(yīng)答和序列號(hào)
  • 2.超時(shí)重傳
  • 3.連接管理
    • 為啥要三次握手,為啥要建立連接?
    • 如果三次握手,握四次行不行,握兩次行不行?
    • 如果三次握手,握兩次行不行?
    • 四次揮手中的ACK和FIN為啥不合并?
  • 4.滑動(dòng)窗口
    • 如果滑動(dòng)窗口的場(chǎng)景中出現(xiàn)丟包了,咋辦?
    • 快速重傳
  • 5.流量控制
  • 6.擁塞控制
  • 7.延時(shí)應(yīng)答
  • 8.捎帶應(yīng)答
  • 9.面向字節(jié)流
    • 如何解決粘包問(wèn)題?
  • 10.TCP中的一些異常情況(心跳機(jī)制)
  • 11.如何基于UDP協(xié)議實(shí)現(xiàn)可靠傳輸?
  • 啥樣的場(chǎng)景中適合用TCP,啥樣的場(chǎng)景中適合用UDP


TCP首部格式

  • 源端口號(hào)
    表示發(fā)送端端口號(hào),字段長(zhǎng)16位
  • 目標(biāo)端口號(hào)
    表示接收端端口號(hào),字段長(zhǎng) 16位
  • 序列號(hào)

字段長(zhǎng)32位。序列號(hào)(有時(shí)也就序號(hào))是指發(fā)送數(shù)據(jù)的位置。每次發(fā)送一次數(shù)據(jù),就累加一次該數(shù)據(jù)字節(jié)數(shù)的大小
序列號(hào)不會(huì)從0或1開(kāi)始,而是建立連接時(shí)有計(jì)算機(jī)生成的隨機(jī)數(shù)作為其初始值,通過(guò)SYN包傳給接收端主機(jī)。然后再將每轉(zhuǎn)發(fā)過(guò)去的字節(jié)數(shù)累加到初始值上表示數(shù)據(jù)的位置。此外,在建立連接和斷開(kāi)連接時(shí)發(fā)送的SYN包和FIN包雖然并不攜帶數(shù)據(jù),但是也會(huì)作為一個(gè)字節(jié)增加對(duì)應(yīng)的序列號(hào)。

  • 確認(rèn)應(yīng)答號(hào)

確認(rèn)應(yīng)答號(hào)字段長(zhǎng)度32位,是指下一次應(yīng)收到的數(shù)據(jù)的序列號(hào),實(shí)際上,它是指已收到確認(rèn)應(yīng)答號(hào)減一為止的數(shù)據(jù),發(fā)送端收到這個(gè)確認(rèn)應(yīng)答以后可以認(rèn)為在這個(gè)序號(hào)以前的數(shù)據(jù)都已經(jīng)被正常接收

  • 數(shù)據(jù)偏移

這個(gè)字段表示所傳輸?shù)臄?shù)據(jù)部分應(yīng)該從TCP包的哪個(gè)位開(kāi)始計(jì)算,也可以把它看作TCP首部的長(zhǎng)度


1.確認(rèn)應(yīng)答和序列號(hào)

確認(rèn)應(yīng)答也是保證可靠性傳輸?shù)暮诵?br /> 發(fā)送方發(fā)數(shù)據(jù)給接收方了,接收方就回應(yīng)一個(gè)應(yīng)答報(bào)文,如果發(fā)送方收到了這個(gè)應(yīng)答報(bào)文,那么認(rèn)為是對(duì)方已經(jīng)收到了。

由于網(wǎng)絡(luò)上的傳輸,順序是不確定的,可能出現(xiàn)后發(fā)先至的情況,因此不能就單純的通過(guò)收到數(shù)據(jù)的順序來(lái)確定邏輯,就需要對(duì)應(yīng)答進(jìn)行的編號(hào)。

實(shí)際上,TCP傳輸數(shù)據(jù),不論條,而是論字節(jié)(面向字節(jié)流)

實(shí)際上,TCP的序號(hào)和確認(rèn)序號(hào),是以字節(jié)為單位進(jìn)行編號(hào)的

比如下面這張圖,第一個(gè)請(qǐng)求A給B發(fā)送了1000個(gè)字節(jié)的數(shù)據(jù),序號(hào)就是1-1000(假設(shè)從1開(kāi)始編號(hào)了),這個(gè)操作相當(dāng)于是發(fā)了一個(gè)TCP數(shù)據(jù)報(bào),這個(gè)數(shù)據(jù)報(bào),這個(gè)數(shù)據(jù)報(bào)的序號(hào)是1,長(zhǎng)度是1000,確認(rèn)應(yīng)答數(shù)據(jù)報(bào),里面的確認(rèn)序號(hào)是1001(意思就是1001之前的數(shù)據(jù),B已經(jīng)收到了,另外,也可以理解成,B在向A索要1001開(kāi)始的數(shù)據(jù))


針對(duì)每個(gè)字節(jié)分別編號(hào)即為序列號(hào),依次進(jìn)行累加(TCP的序號(hào)的起始不一定是從1開(kāi)始的),每個(gè)ACK都帶有對(duì)應(yīng)的確認(rèn)序列號(hào),意思是告訴發(fā)送者,我已經(jīng)收到了哪些數(shù)據(jù),下一次你從哪里開(kāi)始發(fā)


發(fā)送方,就可以根據(jù)確認(rèn)的應(yīng)答報(bào)文來(lái)確定接收方是否是收到了,只要發(fā)送方收到了應(yīng)答,就認(rèn)為接收方已經(jīng)收到,可靠傳輸就完成了。

反之,在一定時(shí)間內(nèi)沒(méi)有等到確認(rèn)應(yīng)答,發(fā)送端就可以認(rèn)為數(shù)據(jù)已經(jīng)丟失,并進(jìn)行重發(fā)。因此,即使產(chǎn)生了丟包,仍然能夠保證數(shù)據(jù)能夠到達(dá)對(duì)端,實(shí)現(xiàn)可靠傳輸

2.超時(shí)重傳

確認(rèn)應(yīng)答機(jī)制中,這個(gè)是比較順利,但是傳輸過(guò)程中還是可能會(huì)出現(xiàn)丟包的,一旦數(shù)據(jù)發(fā)生丟包,就要進(jìn)入超時(shí)重傳機(jī)制中了。

主機(jī)A發(fā)送數(shù)據(jù)給B之后,可能因?yàn)榫W(wǎng)絡(luò)擁堵等原因,數(shù)據(jù)無(wú)法到達(dá)主機(jī)B
如果主機(jī)A在一個(gè)特定時(shí)間間隔內(nèi)沒(méi)有收到B發(fā)來(lái)的確認(rèn)應(yīng)答,就會(huì)進(jìn)行重發(fā)


舉個(gè)列子:

  • A發(fā)消息給B,A發(fā)的消息丟了,B壓根沒(méi)看見(jiàn)
  • A發(fā)的消息過(guò)去了,B也看見(jiàn)了,也回復(fù)了,但是B回復(fù)的消息丟了
    發(fā)送方無(wú)法區(qū)分,當(dāng)前是發(fā)的數(shù)據(jù)丟了,還是應(yīng)答數(shù)據(jù)丟了,發(fā)送方能做的事情,就是在一段時(shí)間之后,重發(fā)一條數(shù)據(jù)
  • 超時(shí)的時(shí)間怎么確定呢?

    • 最理想的情況下,找到一個(gè)最小的時(shí)間,保證 “確認(rèn)應(yīng)答一定能在這個(gè)時(shí)間內(nèi)返回”
    • 但是這個(gè)時(shí)間的長(zhǎng)短,隨著網(wǎng)絡(luò)環(huán)境的不同,是有差異的 。
    • 如果超時(shí)時(shí)間設(shè)的太長(zhǎng),會(huì)影響整體的重傳效率
    • 如果超時(shí)時(shí)間設(shè)的太短,有可能會(huì)頻繁發(fā)送重復(fù)的包

    TCP為了保證無(wú)論在任何環(huán)境下都能比較高性能的通信,因此會(huì)動(dòng)態(tài)計(jì)算這個(gè)最大超時(shí)時(shí)間

    這個(gè)等待,不同的系統(tǒng)實(shí)現(xiàn)的方式不一樣,數(shù)據(jù)在網(wǎng)絡(luò)上傳輸過(guò)程,是需要一定的時(shí)間的,不能說(shuō)數(shù)據(jù)剛發(fā)出去,就期望得到回應(yīng),可能要經(jīng)歷一段時(shí)間之后,才能得到回應(yīng)

    Linux中(BSD Unix和Windows也是如此),超時(shí)以500ms為一個(gè)單位進(jìn)行控制,每次判定超時(shí)重發(fā)的超時(shí)時(shí)間都是500ms的整數(shù)倍 ,發(fā)送方,把數(shù)據(jù)發(fā)出去之后,等待500ms,如果沒(méi)有收到應(yīng)答就認(rèn)為是丟包了。
    如果重發(fā)一次之后,仍然得不到應(yīng)答,等待 2500ms 后再進(jìn)行重傳。
    如果仍然得不到應(yīng)答,等待 4500ms 進(jìn)行重傳。依次類推,以指數(shù)形式遞增
    累計(jì)到一定的重傳次數(shù),TCP認(rèn)為網(wǎng)絡(luò)或者對(duì)端主機(jī)出現(xiàn)異常,強(qiáng)制關(guān)閉連接

    超時(shí)時(shí)間會(huì)動(dòng)態(tài)變化,不是一成不變的。
    等待時(shí)間會(huì)逐漸延長(zhǎng),延長(zhǎng)也就意味著讓重試的頻率盡量降低,只要重傳也失敗,其實(shí)就認(rèn)為,大概率這個(gè)傳輸是通不了的。達(dá)到一定重發(fā)次數(shù)以后,如果仍沒(méi)有任何回應(yīng),就會(huì) 判斷為網(wǎng)絡(luò)或?qū)Χ酥鳈C(jī)發(fā)生了異常,強(qiáng)制關(guān)閉連接,并且通知應(yīng)用通信異常強(qiáng)行終止。

    在超時(shí)重傳的時(shí)候,無(wú)法區(qū)分是發(fā)送過(guò)去的數(shù)據(jù)丟了,還是返回的ACK丟了,就一視同仁了,都要進(jìn)行重傳 ,以500ms為單位,依次增加、

    如果數(shù)據(jù)重復(fù)了怎么辦?

    接收方收到的數(shù)據(jù)會(huì)先放在內(nèi)核的”接收緩沖區(qū)中“,

    接收緩沖區(qū)是一段內(nèi)存,每個(gè)socket都有,按照序號(hào)來(lái)進(jìn)行去重,此時(shí)在應(yīng)用程序中讀取的數(shù)據(jù),讀到的結(jié)果就是不帶重復(fù)的

    3.連接管理

    UDP是一種面向無(wú)連接的通信協(xié)議,因此不檢查對(duì)端是否可以通信,直接將UDP包發(fā)出去,TCP與此相反
    TCP是有連接的,連接管理就是,如何建立連接(三次握手),如何斷開(kāi)連接(四次揮手)

    • 三次握手本質(zhì)上就是:A向B請(qǐng)求連接,B給與回應(yīng),B也向A請(qǐng)求連接,A也給與回應(yīng)

    • 本來(lái)應(yīng)該是“四次握手”,但是中間兩次操作,是可以合在一起的,這兩個(gè)操作在時(shí)間上是同時(shí)發(fā)生的

    • 當(dāng)A的SYN到達(dá)B的時(shí)候,B的內(nèi)核就會(huì)第一時(shí)間進(jìn)行應(yīng)答ACK,同時(shí)也會(huì)第一時(shí)間發(fā)起SYN,這兩件事同時(shí)觸發(fā),于是就沒(méi)有必要分成兩次傳輸,直接一步到位

    為啥要三次握手,為啥要建立連接?

    主要有兩個(gè)目的

  • 投石問(wèn)路,通過(guò)三次握手的過(guò)程,來(lái)確認(rèn)A和B之間的傳輸是通暢的,尤其是要確認(rèn),A和B各自的發(fā)送能力以及接收能力
  • 協(xié)商參數(shù):通過(guò)三次握手,讓A和B之間通通氣,選擇一些傳輸中合適的參數(shù),列如:TCP的序列號(hào)從幾開(kāi)始
  • 如果網(wǎng)絡(luò)出現(xiàn)了問(wèn)題,此時(shí),三次握手都會(huì)難以成功,此時(shí)也就沒(méi)有必要進(jìn)行后續(xù)的傳輸了

    如果三次握手,握四次行不行,握兩次行不行?

    握四次,完全可以。沒(méi)有必要,效果和3次是一樣的。
    中間的ACK和SYN是可以合并在一起的,如果分成兩個(gè),傳輸?shù)拈_(kāi)銷就比一個(gè)大。

    如果三次握手,握兩次行不行?

    握兩次是肯定不行的,A 給 B發(fā)送一個(gè) SYN,B再給A同時(shí)發(fā)送一個(gè)ACK和SYN就完成了兩次握手。

    此時(shí)A知道自己發(fā)送和接收能力沒(méi)有問(wèn)題,也知道B的發(fā)送和接收能力沒(méi)問(wèn)題

    但是B只知道自己的接收能力沒(méi)問(wèn)題,但不知道自己的發(fā)送能力有沒(méi)有問(wèn)題

    B只知道A的發(fā)送能力沒(méi)有問(wèn)題,并不知道A的接收能力有沒(méi)有問(wèn)題。所以并不能進(jìn)行傳輸。


    服務(wù)器和客戶端

    四次揮手中的ACK和FIN為啥不合并?

    對(duì)于B來(lái)說(shuō) ACK 和 FIN 的觸發(fā)時(shí)機(jī)是不一樣的。

  • B只要收到FIN就會(huì)立即觸發(fā)ACK,這個(gè)事事內(nèi)核完成的
  • B發(fā)送FIN的時(shí)機(jī)是用戶代碼控制的(比如代碼中出現(xiàn)了socket,close() 這樣的操作的時(shí)候,才會(huì)觸發(fā)FIN),有可能B的代碼寫(xiě)出問(wèn)題了,有可能一直不調(diào)用close。
    • CLOSE_WAIT:服務(wù)器收到FIN之后,進(jìn)入的狀態(tài),等待用戶代碼調(diào)用close,來(lái)發(fā)送FIN
    • TIME_WAIT:表示的客戶端收到了FIN之后進(jìn)入了TIME_WAIT,這個(gè)狀態(tài)存在的意義主要就是為了處理最后一個(gè)ACK包


    在三次握手和四次揮手的過(guò)程中,同樣可能會(huì)丟包,一旦丟包就會(huì)觸發(fā)超時(shí)重傳

  • 第一個(gè)FIN丟了,A遲遲收不到ACK,就會(huì)重傳FIN
  • 第一個(gè)ACK丟了,A遲遲收不到ACK,就還會(huì)重傳FIN
  • 第二個(gè)FIN丟了,B遲遲收不到ACK,就還是會(huì)重傳FIN
  • 第二個(gè)ACK丟了,B遲遲收不到ACK,還是會(huì)重傳FIN
  • 假設(shè),如果A收到FIN,并返回ACK之后,連接就銷毀,而不是進(jìn)入TIME_WAIT狀態(tài),會(huì)咋樣?

    此時(shí)一旦最后一個(gè)ACK丟了,此時(shí)就無(wú)法重傳ACK了(連接已經(jīng)銷毀了)

    TINE_WAIT即使進(jìn)程已經(jīng)退出了,TIME_WAIT狀態(tài)仍然會(huì)存在(TCP連接不會(huì)立即銷毀),TIME_WAIT 會(huì)等待一定時(shí)間,如果一定時(shí)間之內(nèi)也沒(méi)有重傳的FIN過(guò)來(lái),才會(huì)真正銷毀

    這個(gè)等待時(shí)間為2*MSI

    MSI理論上是,主機(jī)A和B之間最長(zhǎng)的一次通信時(shí)間

    通常在Linux中這個(gè)MSL默認(rèn)是1min,這個(gè)MSL默認(rèn)是1min,當(dāng)然這個(gè)MSL都是可以配置的

    如果服務(wù)器上出現(xiàn)大量的CLOSE_WAIT,是啥情況?

    這是代碼出現(xiàn)bug,close,沒(méi)有及時(shí)被調(diào)用到,

    如果服務(wù)器上出現(xiàn)大量的TIME_WAIT,是啥情況?

    主動(dòng)發(fā)起FIN的一方會(huì)進(jìn)入TIME_WAIT,就需要排查服務(wù)器是否應(yīng)該主動(dòng)斷開(kāi)連接

    這個(gè)也可能是代碼bug,但是不能石錘

    哪方先斷開(kāi)連接,哪方就會(huì)進(jìn)入TIME_WAIT,進(jìn)程退出之后,TIME_WAIT狀態(tài)仍然存在,TCP連接仍然存在

    如果讓服務(wù)器先退出,服務(wù)器這邊就會(huì)進(jìn)入到 TIME_WAIT狀態(tài)(原來(lái)的連接占據(jù)著端口),接下來(lái)如果服務(wù)器立即啟動(dòng),新的進(jìn)程又會(huì)嘗試重新綁定這個(gè)端口可能

    會(huì)存在端口綁定失敗的情況,如果使用原生的socket來(lái)試效果會(huì)非常明顯,第二次啟動(dòng)就會(huì)啟動(dòng)失敗。

    在Java socket,一般來(lái)說(shuō)第二次啟動(dòng)也是能成功的

    在socket api 里有一個(gè) REUSE ADDR 選項(xiàng),如果把這個(gè)選項(xiàng)加上,就能夠讓我們綁定端口的時(shí)候復(fù)用TIME_WAIT狀態(tài)中的端口,(Java socket里面應(yīng)該是默認(rèn)就設(shè)置了這個(gè)選項(xiàng))

    • LISTEN:手機(jī)開(kāi)機(jī) ,信號(hào)良好,隨時(shí)可以打入電話,服務(wù)器的狀態(tài),當(dāng)我們創(chuàng)建好ServerSocket實(shí)例的時(shí)候,就進(jìn)入了LISTEN狀態(tài)
    • ESTABLISHED:接通了電話,雙方可說(shuō)話了,代碼中accept放回了,得到了一個(gè)clientSocke
    • stable:穩(wěn)定的

    四次揮手一定是四次嘛?是否可能是三次呢?

    有可能的,后面的:延時(shí)應(yīng)答和捎帶應(yīng)答雖然ACK和FIN是不同時(shí)機(jī),但是在延時(shí)應(yīng)答和捎帶應(yīng)答的情況下是可能合并在一起的

    四次揮手一定會(huì)執(zhí)行嘛?也不一定

    四次揮手是一個(gè)TCP正常斷開(kāi)的流程,但是實(shí)際上,有的時(shí)候TCP連接也會(huì)異常斷開(kāi)(比如網(wǎng)斷了)

    4.滑動(dòng)窗口

    TCP不僅僅是為了保證可靠性,還要盡可能的提高傳輸效率。
    其實(shí)可靠性和效率,是矛盾的。TCP努力的在可靠性的前提下,又做出了很多性能優(yōu)化的手段

    下圖的這個(gè)發(fā)送過(guò)程,發(fā)送方需要花很多時(shí)間來(lái)等,這個(gè)等待其實(shí)就浪費(fèi)了大量時(shí)間


    現(xiàn)在通過(guò)批量發(fā)送,一次發(fā)送一波,一次等一波的ACK,把多組數(shù)據(jù)的ACK的等待時(shí)間給重疊起來(lái)了。

    一次批量發(fā)的數(shù)據(jù)的長(zhǎng)度,就稱為“窗口大小”

    如果沒(méi)有批量發(fā)送數(shù)據(jù)的長(zhǎng)度限制(窗口無(wú)限大,完全不等,ACK就一頓發(fā)),其實(shí)就沒(méi)有可靠性而言

    如果窗口越大,其實(shí)整體的效率就越高

    如果窗口越小,整體的效率就越低


    當(dāng)前窗口范圍是1001 - 5000,也就意味著,發(fā)送方現(xiàn)在同時(shí)發(fā)送了(1001 - 2000;2001-3000;3001-4000;4001-5000),同時(shí)再等待著四組數(shù)據(jù)的ACK

    假設(shè),2001這個(gè)ACK先到,發(fā)送方就知道了1001-2000這個(gè)數(shù)據(jù)已經(jīng)被對(duì)方收到了

    發(fā)送方也就不用繼續(xù)等這個(gè)數(shù)據(jù)了,接下來(lái)就立即再發(fā)一個(gè)5001 - 6000,仍然保證窗口大小時(shí)4份數(shù)據(jù),仍然保證當(dāng)前同時(shí)等待4份數(shù)據(jù)的ack

    并不是把4份ack都等到,才發(fā)新的數(shù)據(jù),而是隨著收到ack,就隨著往后發(fā)送。

    假設(shè)有后發(fā)先至的情況

    ack 2001,3001,4001,5001 都在網(wǎng)絡(luò)上傳輸呢,不一定非得是2001先到,是否可能3001先到呢?

    這是非常有可能的。

    確認(rèn)序號(hào)表示,從該序號(hào)之前,前面的數(shù)據(jù)都收到了

    如果收到了3001這個(gè)ack,意思就是 1001-2000 和 2001-3000都被對(duì)方收到了,此時(shí)2001這個(gè)ack收或者不收,已經(jīng)不關(guān)鍵。

    如果滑動(dòng)窗口的場(chǎng)景中出現(xiàn)丟包了,咋辦?

    情況1:數(shù)據(jù)包已經(jīng)抵達(dá),ACK丟失了

    這種情況沒(méi)有關(guān)系,只要不是全部ACK丟失就好了,哪怕丟個(gè)50%也沒(méi)事

    發(fā)送方:1001-2000,2001-3000

    接收方:2001,3001

    如果2001這個(gè)acck丟了,3001這個(gè)ack到了

    此時(shí)發(fā)送方也就知道了,3001前面的數(shù)據(jù)都被正確收到了

    此時(shí)1001-2000這個(gè)數(shù)據(jù)報(bào)也得到了一個(gè)確認(rèn)應(yīng)答

    實(shí)際上,TCP為了偷懶(提高效率),滑動(dòng)窗口下,并不是每一條數(shù)據(jù)都有ACK,會(huì)隔幾條數(shù)據(jù)才有一個(gè)ACK

    快速重傳

    快速重傳,效率很高,尤其是不需要重復(fù)傳輸數(shù)據(jù)


    如上圖,因?yàn)?001丟包了,主機(jī)B就會(huì)一直索要1001,確認(rèn)序號(hào)就仍然是1001

    1001-2000這個(gè)數(shù)據(jù)丟了,2001-3000,3001-4000…還在繼續(xù)發(fā)

    發(fā)送方這邊,如果連續(xù)看到幾次1001這個(gè)ACK,就知道了是1001這個(gè)數(shù)據(jù)丟失了,接下來(lái)就會(huì)重傳1001

    前面的2001-7000這些數(shù)據(jù)已經(jīng)到達(dá)了接收端了,值不過(guò)是在接收緩沖區(qū)里待著,當(dāng)1001-2000這個(gè)數(shù)據(jù)到達(dá)的時(shí)候,B就知道了,7001之前的數(shù)據(jù)就都到齊了,此時(shí)就繼續(xù)索要7001這個(gè)數(shù)據(jù)即可

    5.流量控制

    流量控制,本質(zhì)上就是在控制滑動(dòng)窗口的大小,也是保證可靠性的。

    窗口大小決定了傳輸?shù)男?#xff0c;窗口越大,效率就越高,窗口越小,效率就越低。

    既然如此,窗口大小取多少合適呢?

    窗口越大,為了保證可靠性,資源開(kāi)銷就得越多

    窗口太小,速度也得不到保證

    流量控制是基于接收方的處理能力來(lái)限制窗口大小的

    TCP這個(gè)傳輸數(shù)據(jù)的過(guò)程,其實(shí)也就類似于一個(gè)生產(chǎn)者消費(fèi)者模型

    主機(jī)A發(fā)送的數(shù)據(jù)就到達(dá)了主機(jī)B的接收緩沖區(qū),此時(shí)主機(jī)A就是生產(chǎn)者,主機(jī)B的應(yīng)用程序,通過(guò)socket api 來(lái)讀取數(shù)據(jù)。被soket api 讀到的數(shù)據(jù)就從緩沖區(qū)中刪掉了,應(yīng)用程序就是消費(fèi)者,接收緩沖區(qū)就是交易場(chǎng)所(類似于一個(gè)隊(duì)列)

    所說(shuō)的窗口大小,是指發(fā)送發(fā)(主機(jī)A)批量發(fā)多少數(shù)據(jù),比如,主機(jī)A發(fā)的數(shù)據(jù)很快,窗口很大,此時(shí)接收緩沖區(qū)的數(shù)據(jù)也會(huì)增長(zhǎng)很快,如果主機(jī)B的應(yīng)用程序讀取數(shù)據(jù)讀的不快,隨著時(shí)間的推移,接收緩沖區(qū)逐漸就滿了,如果不加任何限制,主機(jī)A還是按照一樣的速度發(fā),此時(shí)新來(lái)的數(shù)據(jù)沒(méi)有地方保存,就被內(nèi)核丟了。

    類似于一個(gè)水池,一邊注水,一邊出水,如果注水速度比出水大,池子很快就滿了

    流量控制這個(gè)機(jī)制,就是為了解決這個(gè)問(wèn)題的,根據(jù)接收方的處理能力(接收緩沖區(qū)的剩余空間大小),來(lái)動(dòng)態(tài)決定發(fā)送方的發(fā)送速率(控制窗大小)


    接收緩沖區(qū)大小是4000

    1-1000數(shù)據(jù)到達(dá)的時(shí)候,緩沖區(qū)這里面用了1000,還剩3000,返回的ack中就會(huì)把3000這個(gè)信息告訴發(fā)送方

    發(fā)送方再次發(fā)送數(shù)據(jù)的時(shí)候,就按照3000作為窗口大小來(lái)進(jìn)行發(fā)送

    窗口大小(接收緩沖區(qū)的剩余空間)是如何放回給發(fā)送方的?

    如果窗口大小為0 了(接收端這邊滿了),然后發(fā)送方就停了嗎?

    此時(shí)發(fā)送方式不再繼續(xù)發(fā)數(shù)據(jù)了,但是為了能夠查詢當(dāng)前接收方的窗口大小。每隔一段時(shí)間,還會(huì)再來(lái)觸發(fā)一個(gè)窗口探測(cè)包,通過(guò)這個(gè)包(不傳輸具體的業(yè)務(wù)數(shù)據(jù)),觸發(fā)ACK,在這個(gè)ACK中就能知道當(dāng)前窗口的大小了

    那么問(wèn)題來(lái)了,16位數(shù)字最大表示65535,那么TCP窗口最大就是65535字節(jié)么?

    64K窗口大小夠嗎?

    TCP首部40字節(jié)選項(xiàng)中還包含了一個(gè)窗口擴(kuò)大因子,實(shí)際窗口大小是 窗口字段的值 左移M位(左移以為相當(dāng)于*2)

    6.擁塞控制

    擁塞控制是站在另外一個(gè)角度來(lái)限制發(fā)送方的窗口大小,站在一個(gè)宏觀角度來(lái)看待這個(gè)問(wèn)題,把整個(gè)中間鏈路都看成了一個(gè)整體,只看結(jié)果。


    先使用一個(gè)比較小的窗口來(lái)傳輸數(shù)據(jù),看看是否丟包,

    如果不丟包,說(shuō)明網(wǎng)絡(luò)比較通暢,如果丟包,說(shuō)明網(wǎng)絡(luò)發(fā)送擁堵

    當(dāng)網(wǎng)絡(luò)通暢的時(shí)候,就逐漸加大發(fā)送速率。當(dāng)網(wǎng)絡(luò)出現(xiàn)丟包的時(shí)候,就立即降低發(fā)送速率。

    通過(guò)這樣的方式,就可以逐漸實(shí)驗(yàn)出一個(gè)比較合適的窗口大小

    真實(shí)的發(fā)送窗口大小 = min(流量控制的窗口,擁塞控制的窗口)

    慢啟動(dòng):剛開(kāi)始啟動(dòng)的時(shí)候,給一個(gè)較小的窗口(比較慢的發(fā)送速率)


    這個(gè)圖描述了擁塞控制中,窗口大小的變化規(guī)則

    指數(shù)增長(zhǎng)速度是非??斓?#xff0c;由于剛開(kāi)始啟動(dòng)的時(shí)候,窗口比較小,此時(shí)通過(guò)指數(shù)增長(zhǎng),就能在很少的輪次中就能把窗口大小給頂上去

    如果達(dá)到閾值就從指數(shù)增長(zhǎng)變成線性增長(zhǎng)

    7.延時(shí)應(yīng)答

    延時(shí)應(yīng)答也是用來(lái)提高效率的(琢磨窗口大小)

    讓窗口大小,在保證可靠的基礎(chǔ)之上,能盡量再大一點(diǎn)

    流量控制來(lái)說(shuō),窗口大小就是接收緩沖區(qū)的剩余空間大小


    主機(jī)A給主機(jī)B發(fā)送數(shù)據(jù),如果接收方立刻放回ACK,此時(shí)放回的窗口大小,就是當(dāng)下這個(gè)緩沖區(qū)剩余的空間。

    但是如果接收方稍等一會(huì),再返回ACK,稍等這個(gè)時(shí)間里,應(yīng)用程序可能就會(huì)消費(fèi)一部分?jǐn)?shù)據(jù),此時(shí)緩沖區(qū)的剩余空間就更大了

    8.捎帶應(yīng)答

    在延時(shí)應(yīng)答的基礎(chǔ)之上

    很多的客戶端/服務(wù)器的通信服務(wù)器的通信模式,都是這種“一問(wèn)一答”

    但是由于有了延時(shí)應(yīng)答,返回的ACK不是立即返回,而是等一會(huì)。

    正好等一會(huì)之后,服務(wù)器要返回業(yè)務(wù)上的response了,此時(shí)就可以把這個(gè)ACK和response合二為一,把兩個(gè)包變成一個(gè)包

    網(wǎng)絡(luò)通信涉及到大量的封裝和分用,針對(duì)每個(gè)包都要一頓封裝,收到之后再一頓解析。


    針對(duì)四次揮手來(lái)說(shuō),確實(shí)是可能四次變成三次的

    捎帶應(yīng)答,是可能吧中間的ACK和FIN給合并成一個(gè),于是四次揮手就變成了三次揮手。

    四次揮手啥時(shí)候能變成三次?這是不能確定的。


    捎帶應(yīng)答本身就是一個(gè)“概率性的機(jī)制”,當(dāng)前ACK延時(shí)的時(shí)間正好要比接下來(lái)發(fā)業(yè)務(wù)數(shù)據(jù)的時(shí)間要更長(zhǎng)一些。

    列如,服務(wù)器收到請(qǐng)求到返回響應(yīng),這個(gè)過(guò)程消耗時(shí)間50ms

    但是延時(shí)應(yīng)答假設(shè)最多等20ms,這個(gè)情況就無(wú)法觸發(fā)捎帶應(yīng)答了

    但是延時(shí)應(yīng)答假設(shè)是最多等60ms

    第50ms的時(shí)候,此時(shí)觸發(fā)了響應(yīng),ACK就可以和這個(gè)響應(yīng)一起過(guò)去了,也就是觸發(fā)了延時(shí)應(yīng)答

    9.面向字節(jié)流

    在這種面向字節(jié)流的情況下,需要注意一個(gè)重要的問(wèn)題:粘包問(wèn)題(指的是應(yīng)用層的數(shù)據(jù)報(bào))

    應(yīng)用程序從接收緩沖區(qū)讀數(shù)據(jù)的時(shí)候,就不知道從哪里到哪里是一個(gè)完整的應(yīng)用層數(shù)據(jù)報(bào)

    應(yīng)用程序此時(shí)只能看到接收緩沖區(qū)中的一個(gè)一個(gè)字節(jié),無(wú)法區(qū)分當(dāng)前接收緩沖區(qū)里有多少個(gè)應(yīng)用層數(shù)據(jù)報(bào),以及從哪到哪是一個(gè)完整的應(yīng)用層數(shù)據(jù)報(bào)

    如何解決粘包問(wèn)題?

    通過(guò)設(shè)計(jì)一個(gè)合理的應(yīng)用層協(xié)議來(lái)解決

  • 給應(yīng)用層數(shù)據(jù)設(shè)定“結(jié)束符”/“分隔符”
  • 給應(yīng)用層數(shù)據(jù)設(shè)定“長(zhǎng)度”
  • 方式一:設(shè)定結(jié)束符,約定每個(gè)應(yīng)用層數(shù)據(jù)報(bào)一定以 ; 結(jié)尾

    方式二:設(shè)定包的長(zhǎng)度,約定每個(gè)應(yīng)用層數(shù)據(jù)報(bào)的前4個(gè)字節(jié),存儲(chǔ)數(shù)據(jù)報(bào)的長(zhǎng)度

    在UDP中是不存在粘包問(wèn)題的

    10.TCP中的一些異常情況(心跳機(jī)制)

    常見(jiàn)的異常情況

  • 進(jìn)程終止

    不管進(jìn)程是咋終止的,本質(zhì)上都會(huì)釋放對(duì)應(yīng)的PCB,也會(huì)釋放對(duì)應(yīng)的文件描述符,一樣會(huì)觸發(fā) 四次揮手

    "進(jìn)程終止"不代表連接就終止,進(jìn)程終止其實(shí)就相當(dāng)于調(diào)用了 soket.close() 方法而已

  • 機(jī)器重啟

    機(jī)器重啟的時(shí)候,其實(shí)也是先殺進(jìn)程,仍然是進(jìn)行四次揮手

  • 機(jī)器掉電、網(wǎng)線斷開(kāi)

    突發(fā)情況,機(jī)器來(lái)不及進(jìn)行任何動(dòng)作的

    如果掉電的是接收方,

    此時(shí)另外一邊還在發(fā)送數(shù)據(jù),此時(shí)顯然發(fā)送方不會(huì)再有ACK,于是就會(huì)超時(shí)重傳.

    重傳幾次之后,就會(huì)嘗試重置連接,這個(gè)時(shí)候,RST(復(fù)位報(bào)文段)就會(huì)設(shè)置為1

    再然后發(fā)送方就會(huì)放棄這個(gè)連接,把連接對(duì)應(yīng)的資源就回收了。

    如果掉電的是發(fā)送方(心跳機(jī)制)

    此時(shí)另外一方在嘗試接收數(shù)據(jù),此時(shí)接收不到任何數(shù)據(jù)

    接收方如何知道,發(fā)送方式掛了?還是說(shuō)發(fā)送方暫時(shí)還沒(méi)發(fā)呢?

    此時(shí)接收方采取的策略,就是"心跳包"機(jī)制(也叫做“?;睢?

    每隔一段時(shí)間,向?qū)Ψ桨l(fā)送一個(gè) PING包,期待對(duì)方返回一個(gè)PONG包。

    如果PING包發(fā)故去,過(guò)了很久還沒(méi)有PONG,并且重試幾次也不行,此時(shí)就認(rèn)為對(duì)方已經(jīng)掛了

    心跳包是一個(gè)應(yīng)用非常廣泛的機(jī)制,不僅僅是在TCP

    在微服務(wù)中,如果某個(gè)主機(jī)宕機(jī)了,此時(shí)入口服務(wù)器就得即使發(fā)現(xiàn)這個(gè)事情,就需要把請(qǐng)求切走

    就可以使用心跳包機(jī)制,直接使用一個(gè)TCP連接時(shí)不行的,雖然使用TCP連接能夠感知是哪個(gè)主機(jī)掛了,但是TCP感知的不夠及時(shí),如果希望能夠更加及時(shí)更快速的發(fā)現(xiàn)問(wèn)題

    就需要在應(yīng)用層實(shí)現(xiàn)心跳機(jī)制

  • 11.如何基于UDP協(xié)議實(shí)現(xiàn)可靠傳輸?

    這個(gè)問(wèn)題其實(shí)是在考TCP

  • 實(shí)現(xiàn)確認(rèn)應(yīng)答機(jī)制,把每個(gè)數(shù)據(jù)接收到了之后,都要反饋一個(gè)ACK(這就不是內(nèi)核返回的了,而是應(yīng)用程序自己定義了一個(gè)ack包發(fā)送過(guò)去)
  • 實(shí)現(xiàn)序號(hào)、確認(rèn)序號(hào),以及實(shí)現(xiàn)去重
  • 實(shí)現(xiàn)超時(shí)重傳
  • 實(shí)現(xiàn)連接管理
  • 想要提高效率,實(shí)現(xiàn)滑動(dòng)窗口
  • 為了限制滑動(dòng)窗口,實(shí)現(xiàn)流量控制、擁塞控制
  • 實(shí)現(xiàn)延時(shí)應(yīng)答,捎帶應(yīng)答,心跳機(jī)制…
  • 啥樣的場(chǎng)景中適合用TCP,啥樣的場(chǎng)景中適合用UDP

  • 如果需要可靠傳輸,肯定首選TCP

  • 如果傳輸單個(gè)數(shù)據(jù)報(bào)比較長(zhǎng)(超過(guò)64K),還是首選TCP

  • 如果特別注重效率,優(yōu)先考慮UDP

    典型的場(chǎng)景:機(jī)房?jī)?nèi)部的主機(jī)通信

    網(wǎng)絡(luò)環(huán)境簡(jiǎn)單,帶寬充裕,丟包的概率不大

    機(jī)房?jī)?nèi)部主機(jī)之間的通信,往往傳輸數(shù)據(jù)量更大,更需要速度

    尤其是在當(dāng)下的"微服務(wù)"這樣的環(huán)境中,其實(shí)特別需要

  • 如果需要廣播,優(yōu)先考慮UDP

    一份數(shù)據(jù)同時(shí)發(fā)給多個(gè)主機(jī)

    UDP自身就支持廣播的

    但是TPC自身不支持廣播,就只能在應(yīng)用程序中,通過(guò)多個(gè)連接,輪詢的方式給每個(gè)主機(jī)發(fā)送數(shù)據(jù)(偽廣播)

  • 除了TPC和UDP之外還有很多其它協(xié)議,有的協(xié)議就可以盡可能的兼顧到可靠性和效率(兼顧可靠性和效率,付出的代價(jià)可能激素會(huì)更多的機(jī)器資源)

    總結(jié)

    以上是生活随笔為你收集整理的网络编程(五) ———— 万字详解TCP协议的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。