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

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

生活随笔

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

编程问答

TCP报文发送的那些事

發(fā)布時(shí)間:2023/12/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TCP报文发送的那些事 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?今天我們來(lái)總結(jié)學(xué)習(xí)一下TCP發(fā)送報(bào)文的相關(guān)知識(shí),主要包括發(fā)送報(bào)文的步驟,MSS,滑動(dòng)窗口和Nagle算法。

發(fā)送報(bào)文

?該節(jié)主要根據(jù)陶輝大神的系列文章總結(jié)而來(lái)。如下圖所示,我們一起來(lái)看一下TCP發(fā)送報(bào)文時(shí)操作系統(tǒng)內(nèi)核都做了那些事情。其中有些概念在接下來(lái)的小節(jié)中會(huì)介紹。

?首先,用戶程序在用戶態(tài)調(diào)用send方法來(lái)發(fā)送一段較長(zhǎng)的數(shù)據(jù)。然后send函數(shù)調(diào)用內(nèi)核態(tài)的tcp_sendmsg方法進(jìn)行處理。

?主要注意的是,send方法返回成功,內(nèi)核也不一定真正將IP報(bào)文都發(fā)送到網(wǎng)絡(luò)中,也就是說(shuō)內(nèi)核發(fā)送報(bào)文和send方法是不同步的。所以,內(nèi)核需要將用戶態(tài)內(nèi)存中的發(fā)送數(shù)據(jù),拷貝到內(nèi)核態(tài)內(nèi)存中,不依賴于用戶態(tài)內(nèi)存,使得進(jìn)程可以快速釋放發(fā)送數(shù)據(jù)占用的用戶態(tài)內(nèi)存。

?在拷貝過(guò)程中,內(nèi)核將待發(fā)送的數(shù)據(jù),按照MSS來(lái)劃分成多個(gè)盡量接近MSS大小的分片,放到這個(gè)TCP連接對(duì)應(yīng)的tcp_write_queue發(fā)送隊(duì)列中。

?內(nèi)核中為每個(gè)TCP連接分配的內(nèi)核緩存,也就是tcp_write_queue隊(duì)列的大小是有限的。當(dāng)沒有多余的空間來(lái)復(fù)制用戶態(tài)的待發(fā)送數(shù)據(jù)時(shí),就需要調(diào)用sk_stream_wait_memory方法來(lái)等待空間,等到滑動(dòng)窗口移動(dòng),釋放出一些緩存出來(lái)(收到發(fā)送報(bào)文相對(duì)應(yīng)的ACK后,不需要再緩存該已發(fā)送出的報(bào)文,因?yàn)榧热灰呀?jīng)確認(rèn)對(duì)方收到,就不需要重發(fā),可以釋放緩存)。

?當(dāng)這個(gè)套接字是阻塞套接字時(shí),等待的超時(shí)時(shí)間就是SO_SNDTIMEO選項(xiàng)指定的發(fā)送超時(shí)時(shí)間。如果這個(gè)套接字是非阻塞套接字,則超時(shí)時(shí)間就是0。也就是說(shuō),sk_stream_wait_memory對(duì)于非阻塞套接字會(huì)直接返回,并將 errno錯(cuò)誤碼置為EAGAIN。

?我們假定使用了阻塞套接字,且等待了足夠久的時(shí)間,收到了對(duì)方的ACK,滑動(dòng)窗口釋放出了緩存。所以,可以將剩下的用戶態(tài)數(shù)據(jù)都組成MSS報(bào)文拷貝到內(nèi)核態(tài)的緩存隊(duì)列中。

?最后,調(diào)用tcp_push等方法,它最終會(huì)調(diào)用IP層的方法來(lái)發(fā)送tcp_write_queue隊(duì)列中的報(bào)文。注意的是,IP層方法返回時(shí),也不意味著報(bào)文發(fā)送了出去。

?在發(fā)送函數(shù)處理過(guò)程中,Nagle算法、滑動(dòng)窗口、擁塞窗口都會(huì)影響發(fā)送操作。

MTU和MSS

?我們都知道TCP/IP架構(gòu)有五層協(xié)議,低層協(xié)議的規(guī)則會(huì)影響到上層協(xié)議,比如說(shuō)數(shù)據(jù)鏈路層的最大傳輸單元MTU和傳輸層TCP協(xié)議的最大報(bào)文段長(zhǎng)度MSS。

?數(shù)據(jù)鏈路層協(xié)議會(huì)對(duì)網(wǎng)絡(luò)分組的長(zhǎng)度進(jìn)行限制,也就是不能超過(guò)其規(guī)定的MTU,例如以太網(wǎng)限制為1500字節(jié),802.3限制為1492字節(jié)。但是,需要注意的時(shí),現(xiàn)在有些網(wǎng)卡具備自動(dòng)分包功能,所以也可以傳輸遠(yuǎn)大于MTU的幀

?網(wǎng)絡(luò)層的IP協(xié)議試圖發(fā)送報(bào)文時(shí),若報(bào)文的長(zhǎng)度大于MTU限制,就會(huì)被分成若干個(gè)小于MTU的報(bào)文,每個(gè)報(bào)文都會(huì)有獨(dú)立的IP頭部。IP協(xié)議能自動(dòng)獲取所在局域網(wǎng)的MTU值,然后按照這個(gè)MTU來(lái)分片。IP協(xié)議的分片機(jī)制對(duì)于傳輸層是透明的,接收方的IP協(xié)議會(huì)根據(jù)收到的多個(gè)IP包頭部,將發(fā)送方IP層分片出的IP包重組為一個(gè)消息。

?這種IP層的分片效率是很差的,因?yàn)槭紫茸隽祟~外的分片操作,然后所有分片都到達(dá)后,接收方才能重組成一個(gè)包,其中任何一個(gè)分片丟失了,都必須重發(fā)所有分片。

?所以,TCP層為了避免IP層執(zhí)行數(shù)據(jù)報(bào)分片定義了最大報(bào)文段長(zhǎng)度MSS。在TCP建立連接時(shí)會(huì)通知各自期望接收到的MSS的大小。

?需要注意的是MSS的值是預(yù)估值。兩臺(tái)主機(jī)只是根據(jù)其所在局域網(wǎng)的計(jì)算MSS,但是TCP連接上可能會(huì)穿過(guò)許多中間網(wǎng)絡(luò),這些網(wǎng)絡(luò)分別具有不同的數(shù)據(jù)鏈路層,導(dǎo)致問(wèn)題。比如說(shuō),若中間途徑的MTU小于兩臺(tái)主機(jī)所在的網(wǎng)絡(luò)MTU時(shí),選定的MSS仍然太大了,會(huì)導(dǎo)致中間路由器出現(xiàn)IP層的分片或者直接返回錯(cuò)誤(設(shè)置IP頭部的DF標(biāo)志位)。

?比如阿里中間件的這篇文章(鏈接不見的話,請(qǐng)看文末)所說(shuō),當(dāng)上述情況發(fā)生時(shí),可能會(huì)導(dǎo)致卡死狀態(tài),比如scp的時(shí)候進(jìn)度卡著不懂,或者其他更復(fù)雜操作的進(jìn)度卡死。

滑動(dòng)窗口

?IP層協(xié)議屬于不可靠的協(xié)議,IP層并不關(guān)心數(shù)據(jù)是否發(fā)送到了接收方,TCP通過(guò)確認(rèn)機(jī)制來(lái)保證數(shù)據(jù)傳輸?shù)目煽啃浴?/p>

?除了保證數(shù)據(jù)必定發(fā)送到對(duì)端,TCP還要解決包亂序(reordering)和流控的問(wèn)題。包亂序和流控會(huì)涉及滑動(dòng)窗口和接收?qǐng)?bào)文的out_of_order隊(duì)列,另外擁塞控制算法也會(huì)處理流控.
?TCP頭里有一個(gè)字段叫Window,又叫Advertised-Window,這個(gè)字段是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個(gè)接收端的處理能力來(lái)發(fā)送數(shù)據(jù),否則會(huì)導(dǎo)致接收端處理不過(guò)來(lái)。

?我們可以將發(fā)送的數(shù)據(jù)分為以下四類,將它們放在時(shí)間軸上統(tǒng)一觀察。

  • Sent and Acknowledged: 表示已經(jīng)發(fā)送成功并已經(jīng)被確認(rèn)的數(shù)據(jù),比如圖中的前31個(gè)字節(jié)的數(shù)據(jù)
  • Send But Not Yet Acknowledged:表示發(fā)送但沒有被確認(rèn)的數(shù)據(jù),數(shù)據(jù)被發(fā)送出去,沒有收到接收端的ACK,認(rèn)為并沒有完成發(fā)送,這個(gè)屬于窗口內(nèi)的數(shù)據(jù)。
  • Not Sent,Recipient Ready to Receive:表示需要盡快發(fā)送的數(shù)據(jù),這部分?jǐn)?shù)據(jù)已經(jīng)被加載到緩存等待發(fā)送,也就是發(fā)送窗口中。接收方ACK表示有足夠空間來(lái)接受這些包,所以發(fā)送方需要盡快發(fā)送這些包。
  • Not Sent,Recipient Not Ready to Receive: 表示屬于未發(fā)送,同時(shí)接收端也不允許發(fā)送的,因?yàn)檫@些數(shù)據(jù)已經(jīng)超出了發(fā)送端所接收的范圍

?除了四種不同范疇的數(shù)據(jù)外,我們可以看到上邊的示意圖中還有三種窗口。

  • Window Already Sent:已經(jīng)發(fā)送了,但是沒有收到ACK,和Send But Not Yet Acknowledged部分重合。
  • Usable Window : 可用窗口,和Not Sent,Recipient Ready to Receive部分重合
  • Send Window: 真正的窗口大小。建立連接時(shí)接收方會(huì)告知發(fā)送方自己能夠處理的發(fā)送窗口大小,同時(shí)在接收過(guò)程中也不斷的通告能處理窗口的大小,來(lái)實(shí)時(shí)調(diào)節(jié)。

?下面,我們來(lái)看一下滑動(dòng)窗口的滑動(dòng)。下圖是滑動(dòng)窗口滑動(dòng)的示意圖。

?當(dāng)發(fā)送方收到發(fā)送數(shù)據(jù)的確認(rèn)消息時(shí),會(huì)移動(dòng)發(fā)送窗口。比如上圖中,接收到36字節(jié)的確認(rèn),將其之前的5個(gè)字節(jié)都移除發(fā)送窗口,然后46-51的字節(jié)發(fā)出,最后將52到56的字節(jié)加入到可用窗口。

?下面我們來(lái)看一下整體的示意圖。


?圖片來(lái)源為tcpipguide.

?client端窗口中不同顏色的矩形塊代表的含義和上邊滑動(dòng)窗口示意圖中相同。我們只簡(jiǎn)單看一下第二三四步。接收端發(fā)送的TCP報(bào)文window為260,表示發(fā)送窗口減少100,可以發(fā)現(xiàn)黑色矩形縮短了,也就是發(fā)送窗口減少了100。并且ack為141,所以發(fā)送端將140個(gè)字節(jié)的數(shù)據(jù)從發(fā)送窗口中移除,這些數(shù)據(jù)從Send But Not Yet Acknowledged變?yōu)镾ent and Acknowledged,也就是從藍(lán)色變成紫色。然后發(fā)送端發(fā)送180字節(jié)的數(shù)據(jù),就有180字節(jié)的數(shù)據(jù)從Not Sent,Recipient Ready to Receive變?yōu)镾end But Not Yet Acknowledged,也就是從綠色變?yōu)樗{(lán)色。

Nagle算法

?上述滑動(dòng)窗口會(huì)出現(xiàn)一種Silly Window Syndrome的問(wèn)題,當(dāng)接收端來(lái)不及取走Receive Windows里的數(shù)據(jù),會(huì)導(dǎo)致發(fā)送端的發(fā)送窗口越來(lái)越小。到最后,如果接收端騰出幾個(gè)字節(jié)并告訴發(fā)送端現(xiàn)在有幾個(gè)字節(jié)的window,而我們的發(fā)送端會(huì)義無(wú)反顧地發(fā)送這幾個(gè)字節(jié)。

?只為了發(fā)送幾個(gè)字節(jié),要加上TCP和IP頭的40多個(gè)字節(jié)。這樣,效率太低,就像你搬運(yùn)物品,明明一次可以全部搬完,但是卻偏偏一次只搬一個(gè)物品,來(lái)回搬多次。

?為此,TCP引入了Nagle算法。應(yīng)用進(jìn)程調(diào)用發(fā)送方法時(shí),可能每次只發(fā)送小塊數(shù)據(jù),造成這臺(tái)機(jī)器發(fā)送了許多小的TCP報(bào)文。對(duì)于整個(gè)網(wǎng)絡(luò)的執(zhí)行效率來(lái)說(shuō),小的TCP報(bào)文會(huì)增加網(wǎng)絡(luò)擁塞的可能。因此,如果有可能,應(yīng)該將相臨的TCP報(bào)文合并成一個(gè)較大的TCP報(bào)文(當(dāng)然還是小于MSS的)發(fā)送。

?Nagle算法的規(guī)則如下所示(可參考tcp_output.c文件里tcp_nagle_check函數(shù)注釋):

  • 如果包長(zhǎng)度達(dá)到MSS,則允許發(fā)送;
  • 如果該包含有FIN,則允許發(fā)送;
  • 設(shè)置了TCP_NODELAY選項(xiàng),則允許發(fā)送;
  • 未設(shè)置TCP_CORK選項(xiàng)時(shí),若所有發(fā)出去的小數(shù)據(jù)包(包長(zhǎng)度小于MSS)均被確認(rèn),則允許發(fā)送;
  • 上述條件都未滿足,但發(fā)生了超時(shí)(一般為200ms),則立即發(fā)送。

?當(dāng)對(duì)請(qǐng)求的時(shí)延非常在意且網(wǎng)絡(luò)環(huán)境非常好的時(shí)候(例如同一個(gè)機(jī)房?jī)?nèi)),Nagle算法可以關(guān)閉。使用TCP_NODELAY套接字選項(xiàng)就可以關(guān)閉Nagle算法

?訂閱最新文章,歡迎關(guān)注我的微信公眾號(hào)

個(gè)人博客: Remcarpediem

個(gè)人微信公眾號(hào): 地址

參考

  • 阿里中間件 http://jm.taobao.org/2017/07/27/20170727/

總結(jié)

以上是生活随笔為你收集整理的TCP报文发送的那些事的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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