偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

瀏覽器輸入一個(gè)網(wǎng)址發(fā)生了什么(二): TCP模塊封裝和傳輸機(jī)制

網(wǎng)絡(luò) 通信技術(shù) 瀏覽器
本節(jié)將介紹http報(bào)文在協(xié)議棧中如何進(jìn)一步處理并發(fā)送到網(wǎng)絡(luò)中。這里說(shuō)的協(xié)議棧是指TCP/IP協(xié)議棧。

本系列文章開(kāi)始將圍繞著“往瀏覽器輸入網(wǎng)址后發(fā)生了什么”介紹計(jì)算機(jī)網(wǎng)絡(luò)的相關(guān)基礎(chǔ)知識(shí)。上節(jié)簡(jiǎn)單的介紹了http報(bào)文封裝和dns請(qǐng)求獲取目標(biāo)IP。

接上文:瀏覽器輸入一個(gè)網(wǎng)址發(fā)生了什么(一) 揭秘DNS請(qǐng)求和解析全流程

本節(jié)將介紹http報(bào)文在協(xié)議棧中如何進(jìn)一步處理并發(fā)送到網(wǎng)絡(luò)中。這里說(shuō)的協(xié)議棧是指TCP/IP協(xié)議棧。

對(duì)于TCP傳輸而言,請(qǐng)求的發(fā)送和接收需要通過(guò)連接進(jìn)行;而UDP傳輸則不用;本節(jié)會(huì)先介紹TCP傳輸?shù)倪^(guò)程再介紹UDP傳輸。

在此之前,需要先介紹一些相關(guān)概念。

一、協(xié)議棧

協(xié)議棧是由多個(gè)具有層級(jí)的協(xié)議模塊組合而成的一個(gè)軟件程序。每個(gè)協(xié)議模塊通常都要和上下層的兩個(gè)其他協(xié)議模塊通信。最低級(jí)的協(xié)議總是描述與物理硬件交互,為每個(gè)高級(jí)的層次增加更多的特性。

協(xié)議是計(jì)算機(jī)在網(wǎng)絡(luò)通信時(shí)需要遵循的規(guī)范和約定,而協(xié)議棧則是這種規(guī)范和約定的實(shí)現(xiàn),也就是具體的代碼和函數(shù)以供上層調(diào)用。每一層協(xié)議模塊都是為上一層協(xié)議模塊服務(wù),本層的協(xié)議模塊完成某個(gè)操作只需要調(diào)用下層模塊的方法而無(wú)需關(guān)注其具體怎么實(shí)現(xiàn)。

二、套接字與連接

對(duì)于TCP傳輸而言,請(qǐng)求的發(fā)送和接收需要通過(guò)連接進(jìn)行。我們可以把連接看做是一條端到端之間的管道,這條管道的兩端都既可以是發(fā)送方也可以是接收方,數(shù)據(jù)在這條管道是雙向流動(dòng)的。而建立這條管道的出入口我們稱(chēng)之為套接字,分別在客戶(hù)端和服務(wù)端兩方,兩方需要先創(chuàng)建套接字才能建立連接。連接建立后,請(qǐng)求和響應(yīng)會(huì)通過(guò)套接字發(fā)出并通過(guò)這個(gè)管道進(jìn)行傳輸。

當(dāng)然管道只是一個(gè)比喻,現(xiàn)實(shí)中是不存在這么一個(gè)管道。

對(duì)于UDP傳輸,雖然不需要建立連接,但是依舊要?jiǎng)?chuàng)建套接字,并通過(guò)套接字發(fā)送和接收包。

三、什么是套接字

套接字本質(zhì)是存放諸如IP地址、端口號(hào)、通信操作狀態(tài)這樣的通信控制信息的內(nèi)存空間里的數(shù)據(jù)。協(xié)議棧在執(zhí)行操作時(shí)會(huì)用到這些控制信息,如封裝報(bào)文時(shí)需要知道通信對(duì)象的IP和端口,如協(xié)議棧需要知道數(shù)據(jù)發(fā)送后經(jīng)過(guò)了多長(zhǎng)時(shí)間仍未返回以便重發(fā),如比對(duì)序號(hào)是否正確,如保存窗口大小從而將窗口大小帶到TCP頭部通知通信的另一方,這都要查詢(xún)套接字的控制信息得知。

協(xié)議棧是根據(jù)套接字中記錄的控制信息來(lái)工作的。

在windows下執(zhí)行netstat可以顯示套接字內(nèi)容。如下圖所示,每一行就是一個(gè)套接字:

  • 第一列:協(xié)議,通常為T(mén)CP或UDP,表示該套接字是為T(mén)CP傳輸還是UDP傳輸而創(chuàng)建的。
  • 第二列:本地端的ip和端口;如果安裝了多塊網(wǎng)卡則會(huì)顯示不同的IP;0.0.0.0表示不綁定IP地址。
  • 第三列:遠(yuǎn)程端的IP和端口;0.0.0.0表示還未開(kāi)始通信,沒(méi)有綁定IP和端口;此外,UDP協(xié)議中的套接字不綁定對(duì)方地址和端口因此顯示*:*。
  • 第四列:通信狀態(tài);LISTENING 等待對(duì)方連接;ESTABLISHED 完成連接正在通信。
  • 第五列:使用該套接字程序的進(jìn)程ID。

回到正題。

當(dāng)客戶(hù)端程序(瀏覽器)拿到目標(biāo)IP地址后,會(huì)經(jīng)歷:創(chuàng)建套接字、連接、發(fā)送數(shù)據(jù)、接收數(shù)據(jù)、斷開(kāi)連接、刪除套接字。如下圖所示

1.創(chuàng)建套接字

應(yīng)用程序(瀏覽器)調(diào)用socket()方法,然后工作會(huì)切到操作系統(tǒng)的協(xié)議棧進(jìn)行。協(xié)議棧會(huì)先分配一個(gè)用于存放套接字的內(nèi)存空間,并向其寫(xiě)入初始狀態(tài),協(xié)議棧會(huì)返回一個(gè)代表套接字的描述符(一個(gè)整型)給瀏覽器。

之后當(dāng)瀏覽器需要收發(fā)數(shù)據(jù)時(shí)(connect/read/write)就需要向協(xié)議棧提供這個(gè)描述符。協(xié)議棧會(huì)根據(jù)這個(gè)描述符找到對(duì)應(yīng)的套接字進(jìn)行操作。

2.建立連接

創(chuàng)建套接字后,應(yīng)用程序(瀏覽器)會(huì)調(diào)用connect方法,工作會(huì)切換到操作系統(tǒng)的協(xié)議棧,由協(xié)議棧負(fù)責(zé)將本地的套接字和服務(wù)器的套接字進(jìn)行連接。

連接的本質(zhì)是通信雙方交換控制信息并記錄到雙方套接字中,之后的每一次數(shù)據(jù)收發(fā)時(shí)數(shù)據(jù)包都要帶上套接字中的控制信息。例如在本地套接字中記錄下服務(wù)器的ip和端口,并告知服務(wù)器請(qǐng)求方的ip和端口,讓服務(wù)端把客戶(hù)端的ip端口記錄到服務(wù)端套接字中,此外還有其他的控制信息。

另外,連接操作過(guò)程中,通信雙方還會(huì)分配一塊用于臨時(shí)存放收發(fā)數(shù)據(jù)的內(nèi)存空間。

四、TCP控制信息(TCP頭部)

上面所說(shuō)的控制信息會(huì)寫(xiě)入到TCP報(bào)文的頭部發(fā)送到服務(wù)端,如下所示:

上面這些字段都是保存在套接字中,并且在需要構(gòu)建頭部的時(shí)候從套接字中取出這些信息。再通信雙方的不斷通信的過(guò)程中會(huì)更新到套接字中(如窗口和序號(hào))。

五、TCP傳輸?shù)臄?shù)據(jù)包

無(wú)論是連接、收發(fā)數(shù)據(jù)和斷開(kāi)連接,通信雙方都需要通過(guò)發(fā)送數(shù)據(jù)包來(lái)進(jìn)行。

對(duì)于連接和斷開(kāi)連接而言,無(wú)需傳遞應(yīng)用程序(瀏覽器)數(shù)據(jù),因此數(shù)據(jù)包只用包含控制信息(也就是頭部信息)。而收發(fā)數(shù)據(jù)時(shí)的包會(huì)包含控制信息和數(shù)據(jù)塊內(nèi)容。應(yīng)用程序的數(shù)據(jù)可能會(huì)很大,因此協(xié)議棧會(huì)將數(shù)據(jù)切分為一個(gè)個(gè)數(shù)據(jù)塊,每個(gè)數(shù)據(jù)塊都添加上頭部控制信息做成包再發(fā)送。

如下所示:

下面我們正式介紹連接的過(guò)程:

  • 應(yīng)用程序(瀏覽器)調(diào)用socket庫(kù)的connect(<套接字的描述符>, <服務(wù)端的IP和端口>, ...)
  • 工作切換到TCP/IP協(xié)議棧,協(xié)議棧構(gòu)建包含諸多控制信息(通信雙方的端口,窗口,序號(hào),ACK號(hào)等)的TCP頭部(TCP報(bào)文),并將頭部中的SYN比特位置為1,表示該報(bào)文是向服務(wù)端請(qǐng)求建立連接而建的。
  • TCP模塊將構(gòu)建好的TCP報(bào)文(該報(bào)文只包含頭部信息,沒(méi)有數(shù)據(jù)內(nèi)容)交給IP模塊,由IP模塊進(jìn)一步封裝成包(添加IP頭部和MAC頭部),IP模塊再通過(guò)網(wǎng)卡將包轉(zhuǎn)為電信號(hào),借由網(wǎng)線(xiàn)傳輸給服務(wù)端。(包是如何根據(jù)IP地址找到服務(wù)器,以及包在網(wǎng)絡(luò)中的傳輸過(guò)程會(huì)在后面介紹)。
  • 包到達(dá)服務(wù)端之后(通過(guò)網(wǎng)線(xiàn)、網(wǎng)卡到達(dá)服務(wù)端協(xié)議棧),根據(jù)報(bào)文中指定的端口找到服務(wù)端對(duì)應(yīng)的套接字。服務(wù)端的套接字從監(jiān)聽(tīng)狀態(tài)變成正在連接的狀態(tài),并且還會(huì)將報(bào)文中的控制信息寫(xiě)入到套接字中保存。
  • 之后服務(wù)端的協(xié)議棧也會(huì)封裝響應(yīng)報(bào)文,該過(guò)程和上述A/B/C步驟類(lèi)似,并且報(bào)文中的TCP頭部的SYN和ACK位置為1,意味著服務(wù)端向發(fā)送方發(fā)起連接請(qǐng)求(SYN=1)和接收到發(fā)送方的包(ACK=1)。
  • 響應(yīng)包到達(dá)發(fā)送方后,協(xié)議棧會(huì)把套接字的連接狀態(tài)改為已連接,并將服務(wù)端的IP和端口保存到套接字中。然后封裝一個(gè)ACK為1的包給服務(wù)端表示接受到服務(wù)端的連接請(qǐng)求包。
  • 服務(wù)端接收到客戶(hù)端的ACK為1的包后連接操作才算完成。

到此為止,客戶(hù)端和服務(wù)端的套接字都保存了對(duì)方的信息,控制流程從操作系統(tǒng)回到了應(yīng)用程序,之后可以開(kāi)始真正發(fā)送客戶(hù)端的http消息了。在執(zhí)行close之前,連接會(huì)一直保持。

上述雙方交換3次數(shù)據(jù)包的過(guò)程也是大家熟悉的“三次握手”過(guò)程。

http消息封裝成網(wǎng)絡(luò)包發(fā)送

連接完成以后,應(yīng)用程序(瀏覽器)會(huì)調(diào)用socket庫(kù)的write()方法將數(shù)據(jù)(封裝好的http消息)傳遞給協(xié)議棧,由協(xié)議棧發(fā)送給服務(wù)端。

在講數(shù)據(jù)收發(fā)流程之前,需要先介紹一些數(shù)據(jù)收發(fā)的細(xì)節(jié)和重點(diǎn):

六、網(wǎng)絡(luò)包

包是網(wǎng)絡(luò)數(shù)據(jù)在網(wǎng)絡(luò)層(如IP包和以太網(wǎng)包)的稱(chēng)呼。

包是由記錄了控制信息的頭部和包的內(nèi)容組成。IP頭部和MAC頭部是在網(wǎng)絡(luò)層添加的,因此都能被稱(chēng)為包:

TCP模塊負(fù)責(zé)將http消息加上TCP頭部控制信息,并將這些數(shù)據(jù)傳遞給下一層的IP模塊,由IP模塊封裝IP頭部和MAC頭部形成網(wǎng)絡(luò)包,其中TCP頭部記錄著通信雙方的端口號(hào),IP頭部記錄著通信雙方的IP。無(wú)論是連接階段的數(shù)據(jù)還是數(shù)據(jù)收發(fā)的數(shù)據(jù)都會(huì)經(jīng)過(guò)IP模塊的封裝和發(fā)送。

IP頭部:

PS:IP地址不是分配給計(jì)算機(jī)的而是分配給網(wǎng)卡的。因此如果計(jì)算機(jī)內(nèi)包含多塊網(wǎng)卡就可以擁有多個(gè)IP地址。如果客戶(hù)端和服務(wù)端主機(jī)有多個(gè)IP地址,那么在IP頭部中填寫(xiě)哪個(gè)IP地址取決于協(xié)議棧想讓哪塊網(wǎng)卡發(fā)送數(shù)據(jù)包,并把數(shù)據(jù)包發(fā)送到服務(wù)器的哪塊網(wǎng)卡上。

MAC頭部:

下章節(jié)再對(duì)IP頭部和MAC頭部進(jìn)行介紹。這里我們只需要知道添加了這兩個(gè)頭部之后數(shù)據(jù)就變成網(wǎng)絡(luò)包,再經(jīng)由IP模塊傳遞給網(wǎng)卡,傳遞給網(wǎng)卡的時(shí)候,網(wǎng)絡(luò)包才算正式離開(kāi)了協(xié)議棧。

其他層的網(wǎng)絡(luò)數(shù)據(jù)稱(chēng)呼:

  • 幀:數(shù)據(jù)在數(shù)據(jù)鏈路層的單位
  • 包:數(shù)據(jù)在網(wǎng)絡(luò)層的單位
  • 段:數(shù)據(jù)在傳輸層的單位
  • 消息:數(shù)據(jù)在應(yīng)用層的單位

七、數(shù)據(jù)發(fā)送時(shí)機(jī)

協(xié)議棧不一定會(huì)在一收到數(shù)據(jù)就馬上發(fā)出去,也可能是先將數(shù)據(jù)存到內(nèi)部的發(fā)送緩沖區(qū),等到積累到一定程度后再發(fā)送。

這樣做的原因是應(yīng)用程序每次傳遞多少數(shù)據(jù)給協(xié)議棧是不定的,有些應(yīng)用程序是逐字或逐行節(jié)傳遞給協(xié)議棧,有些是一次性傳遞整個(gè)請(qǐng)求的所有數(shù)據(jù)給協(xié)議棧,如果應(yīng)用程序每次傳遞給協(xié)議棧的數(shù)據(jù)很少,且協(xié)議棧每接收到一次數(shù)據(jù)就馬上發(fā)送就會(huì)發(fā)送大量的小包導(dǎo)致網(wǎng)絡(luò)效率下降。

每次傳遞多少數(shù)據(jù)給協(xié)議棧是由應(yīng)用程序決定的。

是否讓協(xié)議棧一接收到應(yīng)用程序的數(shù)據(jù)就馬上發(fā)送也是由應(yīng)用程序進(jìn)行系統(tǒng)調(diào)用時(shí)的指定的一些選項(xiàng)決定的。

譬如對(duì)于瀏覽器這種會(huì)話(huà)型應(yīng)用程序而言,它會(huì)指定讓協(xié)議棧馬上發(fā)送數(shù)據(jù)以避免延遲。

如果應(yīng)用程序決定讓協(xié)議棧先緩存數(shù)據(jù)再發(fā)送的話(huà),那么協(xié)議棧會(huì)根據(jù)兩個(gè)要素決定發(fā)送的時(shí)機(jī):一個(gè)是網(wǎng)絡(luò)包能容納的最大數(shù)據(jù)長(zhǎng)度,一個(gè)是應(yīng)用程序傳遞數(shù)據(jù)的頻率(每次傳遞數(shù)據(jù)的時(shí)間間隔)

網(wǎng)絡(luò)包能容納的最大數(shù)據(jù)長(zhǎng)度: 協(xié)議棧規(guī)定一個(gè)網(wǎng)絡(luò)包能容納的最大長(zhǎng)度是MTU(在以太網(wǎng)中是1500字節(jié)),即IP頭部+TCP頭部+真實(shí)數(shù)據(jù) <= 1500。除去頭部,真實(shí)數(shù)據(jù)的最大長(zhǎng)度是MSS = 1500 - 20 - 20 = 1460。當(dāng)協(xié)議棧收到的數(shù)據(jù)接近或超過(guò)MSS的長(zhǎng)度時(shí)再發(fā)出去就可以避免發(fā)送大量小包的問(wèn)題。

應(yīng)用程序傳遞數(shù)據(jù)的時(shí)間間隔:

當(dāng)應(yīng)用程序傳遞消息的頻率不高的時(shí)候,如果每次都要等到長(zhǎng)度接近MSS就會(huì)造成較大的發(fā)送延遲。協(xié)議棧中有一個(gè)計(jì)時(shí)器,當(dāng)經(jīng)過(guò)一定時(shí)間后即便緩沖區(qū)數(shù)據(jù)沒(méi)有達(dá)到MSS也會(huì)將其封裝為包發(fā)送出去。

八、大數(shù)據(jù)拆分

一般GET請(qǐng)求方式的HTTP消息不會(huì)很長(zhǎng),一個(gè)網(wǎng)絡(luò)包(這里的包是指IP模塊發(fā)出去的包)就能裝下。但如果要傳遞表單甚至上傳文件就可能超過(guò)一個(gè)包容納的數(shù)據(jù)量(MSS),此時(shí)發(fā)送緩沖區(qū)的數(shù)據(jù)量會(huì)大于MSS。這時(shí)協(xié)議棧的TCP模塊會(huì)將發(fā)送緩沖區(qū)中的數(shù)據(jù)(也就是http消息)以MSS為單位進(jìn)行拆分為多個(gè)數(shù)據(jù)塊,每個(gè)塊被單獨(dú)添加TCP頭部,單獨(dú)封裝為網(wǎng)絡(luò)包傳輸。

在拆分?jǐn)?shù)據(jù)時(shí),TCP模塊會(huì)計(jì)算好每一塊數(shù)據(jù)的第一個(gè)字節(jié)是整體數(shù)據(jù)的第幾個(gè)字節(jié),以此作為每塊數(shù)據(jù)的TCP頭部中的序號(hào)(序號(hào)是應(yīng)用程序的數(shù)據(jù)序號(hào)而不是網(wǎng)絡(luò)包的長(zhǎng)度序號(hào))。

接收方可以根據(jù)接收到每個(gè)網(wǎng)絡(luò)包的長(zhǎng)度減去頭部長(zhǎng)度得到每個(gè)數(shù)據(jù)塊的長(zhǎng)度。然后通過(guò)當(dāng)前最新序號(hào)+數(shù)據(jù)塊長(zhǎng)度得知下一個(gè)接收到的包的序號(hào)應(yīng)該是多少。接收方返回的應(yīng)答包的ACK號(hào)應(yīng)該是當(dāng)前最新序號(hào)+數(shù)據(jù)塊長(zhǎng)度+1,下一次發(fā)送方應(yīng)該發(fā)送的序號(hào)也應(yīng)該是接收方上一次回包的ACK號(hào)。

例如:當(dāng)前序號(hào)為A,包的長(zhǎng)度是1460。那么響應(yīng)包ACK號(hào)應(yīng)該是A+1460+1 。接收方應(yīng)該接收到的下一個(gè)包的序號(hào)應(yīng)該是A+1460+1 = A + 1461。如果接收方收到的下個(gè)包的序號(hào)比A+1461大,就說(shuō)明中間有包遺漏。

序號(hào)告訴接收方當(dāng)前發(fā)送方已經(jīng)發(fā)送了多少字節(jié)的數(shù)據(jù),ACK告訴發(fā)送方當(dāng)前接收方接收了多少字節(jié)的數(shù)據(jù)(所以同理,發(fā)送方能通過(guò)ACK號(hào)判斷接收方的回包是否有丟包)。

每次發(fā)送方發(fā)出的網(wǎng)絡(luò)包都會(huì)得到接收方包含ACK號(hào)的回包,這種機(jī)制成為確認(rèn)應(yīng)答機(jī)制。

實(shí)際通信中,序號(hào)不是從1開(kāi)始,而是用隨機(jī)數(shù)計(jì)算出來(lái)的一個(gè)初始值。在連接階段,雙方設(shè)置SYN為1的時(shí)候,初始的序號(hào)也會(huì)被設(shè)置好并由通信雙方互相發(fā)送給對(duì)方(同理初始ACK號(hào)也在連接階段通知給對(duì)方)。

也就是說(shuō),通信的時(shí)候序號(hào)是有兩個(gè),一個(gè)是客戶(hù)端生成的序號(hào),一個(gè)是服務(wù)端生成的序號(hào),因?yàn)門(mén)CP數(shù)據(jù)的收發(fā)是雙向的(例如服務(wù)端會(huì)接收到客戶(hù)端的包時(shí)回包,服務(wù)端也會(huì)在返回響應(yīng)數(shù)據(jù)時(shí)主動(dòng)發(fā)送包[這里不是指ACK包,而是返回http響應(yīng)消息的網(wǎng)絡(luò)包],所以客戶(hù)端不只是發(fā)送方,服務(wù)端不只是接收方,應(yīng)該是客戶(hù)端和服務(wù)端都既能是發(fā)送方也能是接收方)。同理ACK號(hào)也有兩個(gè)。

如下:

序號(hào)和ACK號(hào)的出現(xiàn)是為了告訴通信雙方每次發(fā)包發(fā)送了多少數(shù)據(jù)和接收了多少數(shù)據(jù)從而判斷是否有丟包。丟包會(huì)引發(fā)TCP的重傳機(jī)制,重傳是TCP模塊獨(dú)有的錯(cuò)誤補(bǔ)償機(jī)制,網(wǎng)卡、路由器、集線(xiàn)器一旦檢測(cè)到錯(cuò)誤會(huì)直接丟棄包而不會(huì)重傳。

九、重傳機(jī)制之ACK號(hào)等待時(shí)間

當(dāng)發(fā)送方發(fā)送包后會(huì)等待接收方返回帶有ACK號(hào)的回包(在這個(gè)等待過(guò)程中發(fā)送方不會(huì)什么都不做,這里涉及到后面要介紹的滑動(dòng)窗口,這里先不提),這段等待的時(shí)間叫做ACK號(hào)的等待時(shí)間,如果超過(guò)了這段時(shí)間都沒(méi)有接收到回包,發(fā)送方會(huì)認(rèn)為這個(gè)包在網(wǎng)絡(luò)中丟失,因而重新發(fā)送這個(gè)包。

發(fā)送方丟包 或者 發(fā)送方的包到達(dá)接收方后接收方的回包丟包 或者 接收方的回包因?yàn)榫W(wǎng)絡(luò)擁塞而返回緩慢都可能造成接收方等待超過(guò)ACK號(hào)的超時(shí)時(shí)間而引起重傳。

TCP模塊怎么設(shè)置一個(gè)合適的ACK等待時(shí)間?

由于不同服務(wù)器的距離不同,或者不同網(wǎng)絡(luò)環(huán)境下網(wǎng)絡(luò)擁塞的情況不同(如局域網(wǎng)內(nèi)可能幾毫秒能返回ACK號(hào),而在互聯(lián)網(wǎng)中遇到擁塞可能幾百毫秒才能返回),ACK等待時(shí)間不是一個(gè)固定的時(shí)間,而是動(dòng)態(tài)變化的時(shí)間。TCP模塊會(huì)持續(xù)測(cè)量多次ACK號(hào)的返回時(shí)間,如果前幾次ACK號(hào)返回變慢就會(huì)延長(zhǎng)等待時(shí)間,如果前幾次ACK號(hào)能馬上返回則縮短等待時(shí)間。

每次因?yàn)槌^(guò)等待時(shí)間導(dǎo)致的重傳會(huì)延長(zhǎng)這個(gè)超時(shí)時(shí)間,如果多次重傳后仍未收到確認(rèn)包則會(huì)斷開(kāi)連接。

十、重傳機(jī)制之快速重傳

快速重傳是比超時(shí)重傳更有效率的重傳方式,當(dāng)接收方接收到亂序的報(bào)文(如 1-3-2,或者1-3-4)時(shí),會(huì)立刻不延遲的返回重復(fù)ACK的回包給發(fā)送方,發(fā)送方重復(fù)接收到3次相同的ACK號(hào)之后就會(huì)重發(fā)丟失的包。如下所示:

圖中發(fā)送方第一個(gè)包到達(dá),接收方返回ACK號(hào)為ACK2的回包;但發(fā)送方的第二個(gè)報(bào)文丟失,之后第3~5個(gè)包到達(dá)接收方,接收方通過(guò)確認(rèn)包的序號(hào)得知第二個(gè)包沒(méi)有發(fā)送過(guò)來(lái),因此對(duì)第3~5的包返回重復(fù)的ACK號(hào)(ACK2)。

接收方接收到多次重復(fù)的ACK號(hào)為ACK2的響應(yīng)包之后得知第2個(gè)包沒(méi)有發(fā)送到接收方,就會(huì)重新發(fā)送第2個(gè)包(序號(hào)為ACK2的包),并且重發(fā)第3~5個(gè)包(因?yàn)榘l(fā)送方得到的最新ACK號(hào)是ACK2,因此會(huì)重發(fā)序號(hào)為ACK2以后的所有包)。接收方接收到第2~5個(gè)包后,統(tǒng)一回復(fù)了一個(gè)ACK號(hào)為ACK6的包。

如果使用了SACK選項(xiàng),發(fā)送方只會(huì)重發(fā)第2個(gè)包,而不會(huì)重發(fā)3~5。

十一、簡(jiǎn)述滑動(dòng)窗口

如果TCP模塊發(fā)送一個(gè)包并等待回包的過(guò)程中什么都不做會(huì)顯得十分浪費(fèi)。為了減少這樣的浪費(fèi),TCP模塊使用滑動(dòng)窗口的方式發(fā)送包,也就是說(shuō)它不會(huì)非得等到一個(gè)包回包之后才發(fā)下一個(gè)包,而是連續(xù)發(fā)送多個(gè)包并連續(xù)接收多個(gè)回包。如下所示:

接收方在接收到包會(huì)先存放到接收緩沖區(qū),TCP模塊會(huì)從緩沖區(qū)中獲取包并計(jì)算包的ACK號(hào),將多個(gè)包的數(shù)據(jù)塊組裝起來(lái)還原為原本的數(shù)據(jù)并傳遞給接收方的應(yīng)用程序。

如果包到達(dá)的速度要比數(shù)據(jù)處理并傳遞給應(yīng)用程序的速度快,那么緩沖區(qū)中數(shù)據(jù)就會(huì)越積越多最后溢出,溢出之后,接收方就無(wú)法接受后面的包。

如下圖所示,當(dāng)接收方收到包之后,會(huì)馬上從接收緩沖區(qū)中取出包并處理,緩沖區(qū)的空間會(huì)得到釋放,然后在返回包的TCP頭部注明接收緩沖區(qū)的剩余空間(也是窗口大?。?,這樣發(fā)送方收到回包后就知道下次發(fā)送的數(shù)據(jù)量不要超過(guò)這個(gè)窗口的大小的數(shù)據(jù)量。

在接收方不斷發(fā)送數(shù)據(jù)的過(guò)程中,它會(huì)自動(dòng)計(jì)算自己用掉了多少窗口大小,當(dāng)計(jì)算到窗口大小為0時(shí)會(huì)暫停發(fā)送包。在這個(gè)過(guò)程中如果接收方回包,發(fā)送方會(huì)根據(jù)回包中的TCP頭部窗口大小來(lái)更新自己套接字內(nèi)的窗口大小(自動(dòng)計(jì)算過(guò)程為4380->2920->1460,此時(shí)接收方返回ACK包并告知窗口大小剩余2920,發(fā)送方會(huì)更新當(dāng)前窗口大小為2920,再?gòu)?920繼續(xù)自動(dòng)計(jì)算,2920->1460->0,此時(shí)暫停,直到接收方的ACK包又到達(dá)發(fā)送方,發(fā)送方再更新窗口大小,又開(kāi)始繼續(xù)發(fā)包)。這就是滑動(dòng)窗口的基本思路。

這張圖是為了講解方便,故意體現(xiàn)一種接收方來(lái)不及處理收到的包,導(dǎo)致緩沖區(qū)被填滿(mǎn)的情況。實(shí)際上,接收方在收到數(shù)據(jù)之后馬上就會(huì)開(kāi)始進(jìn)行處理,如果接收方的性能高,處理速度比包的到達(dá)速率還快,緩沖區(qū)馬上就會(huì)被清空,并通過(guò)窗口字段告知發(fā)送方。

還有,圖中只顯示了從右往左發(fā)送數(shù)據(jù)的操作,實(shí)際上和序號(hào)、ACK號(hào)一樣,發(fā)送操作也是雙向進(jìn)行的。

另外需要注意的是:接收方不會(huì)對(duì)發(fā)送方的每個(gè)包都發(fā)送回包,因?yàn)榻邮辗桨l(fā)送回包其實(shí)是為了通知發(fā)送方ACK號(hào)和窗口大小(也就是告訴發(fā)送方我收到了多少包以及我還能接收你多少包),所以接收方在一段時(shí)間內(nèi)連續(xù)收到發(fā)送方多個(gè)包后可能只會(huì)發(fā)送一個(gè)回包里面記錄著當(dāng)前最新的ACK號(hào)(即最后一個(gè)來(lái)包對(duì)應(yīng)的ACK號(hào))和最新的窗口大小,中間的ACK號(hào)會(huì)省略,這樣減少了包的數(shù)量提升了網(wǎng)絡(luò)通信的效率。

當(dāng)服務(wù)端接收完客戶(hù)端某個(gè)HTTP消息的所有包并還原為http消息時(shí),服務(wù)端的應(yīng)用程序就對(duì)這個(gè)http請(qǐng)求進(jìn)行處理,再響應(yīng)數(shù)據(jù)封裝為http響應(yīng)消息,經(jīng)過(guò)協(xié)議棧封裝成一個(gè)個(gè)包返回給客戶(hù)端。這時(shí)服務(wù)端變成了發(fā)送方,而客戶(hù)端變成接收方。

1.接收http響應(yīng)消息

當(dāng)協(xié)議棧將http消息以多個(gè)包的形式發(fā)送完畢之后,工作流程回到應(yīng)用程序(瀏覽器),應(yīng)用程序會(huì)調(diào)用read()接收響應(yīng)消息。工作流程再次轉(zhuǎn)移到協(xié)議棧。

響應(yīng)消息到達(dá)客戶(hù)端主機(jī)后會(huì)先暫存在接收緩沖區(qū),協(xié)議棧會(huì)嘗試從緩沖區(qū)獲取數(shù)據(jù),如果緩沖區(qū)中沒(méi)有數(shù)據(jù),則協(xié)議棧會(huì)暫停接收工作去做其他事情。直到消息到達(dá)才會(huì)將數(shù)據(jù)傳遞給應(yīng)用程序,即把數(shù)據(jù)復(fù)制到應(yīng)用程序指定的內(nèi)存地址(數(shù)據(jù)量大的情況下不會(huì)一次性全傳遞給應(yīng)用程序,而是會(huì)分多次傳遞)。

在這一步中,還略過(guò)了很多的細(xì)節(jié),如協(xié)議棧會(huì)檢查收到的數(shù)據(jù)塊和TCP頭部,判斷是否有數(shù)據(jù)丟失;向服務(wù)器方更新窗口大?。粚⒍鄩K數(shù)據(jù)按照序號(hào)拼接為原始的數(shù)據(jù),等等。這些過(guò)程和客戶(hù)端發(fā)送發(fā)送方時(shí)的過(guò)程類(lèi)似。

2.服務(wù)器斷開(kāi)連接并刪除套接字

發(fā)送完數(shù)據(jù)的一方會(huì)主動(dòng)發(fā)起斷開(kāi)連接的操作。在Web通信中,服務(wù)器一方是最后發(fā)送數(shù)據(jù)的一方因此會(huì)主動(dòng)關(guān)閉連接。

  • 服務(wù)器會(huì)應(yīng)用程序會(huì)調(diào)用Socket庫(kù)的close函數(shù),工作流程轉(zhuǎn)到協(xié)議棧,協(xié)議棧會(huì)生成FIN比特位為1的TCP頭部,委托IP模塊發(fā)送給客戶(hù)端。此時(shí)服務(wù)器會(huì)進(jìn)入關(guān)閉等待狀態(tài)并記錄到套接字中。
  • 客戶(hù)端收到服務(wù)端關(guān)閉連接的包后,會(huì)進(jìn)入關(guān)閉等待的狀態(tài)并記錄到套接字中。并且發(fā)送一個(gè)ACK包給服務(wù)端表示已經(jīng)收到了關(guān)閉連接的請(qǐng)求。
  • 客戶(hù)端不會(huì)馬上關(guān)閉連接,而是等應(yīng)用程序?qū)⒔邮站彌_區(qū)中所有數(shù)據(jù)接收完畢(等待read()過(guò)程結(jié)束)。
  • 當(dāng)操作系統(tǒng)中接收緩沖區(qū)的數(shù)據(jù)接收完畢后,工作流程回到應(yīng)用程序,應(yīng)用程序調(diào)用Socket庫(kù)的close函數(shù),工作流程轉(zhuǎn)到協(xié)議棧,客戶(hù)端的協(xié)議棧也會(huì)生成一個(gè)FIN比特位為1的TCP包通過(guò)IP模塊發(fā)送給服務(wù)端,告訴服務(wù)端可以關(guān)閉連接了。
  • 服務(wù)端收到FIN比特位為1的TCP包后會(huì)回一個(gè)ACK包??蛻?hù)端接收到ACK包后會(huì)關(guān)閉連接(關(guān)閉連接的實(shí)質(zhì)是刪除套接字),而服務(wù)端會(huì)不會(huì)馬上關(guān)閉連接(即不會(huì)馬上刪除套接字),而是進(jìn)入到time-wait的等待狀態(tài)(將socket的狀態(tài)標(biāo)記為time-wait),time-wait狀態(tài)結(jié)束后服務(wù)器才會(huì)真正關(guān)閉連接和刪除套接字。

以上過(guò)程就是大家熟知的“四次揮手”過(guò)程。

服務(wù)端不馬上不關(guān)閉連接而是進(jìn)入time-wait狀態(tài)是為了防止誤操作。例如當(dāng)客戶(hù)端是主動(dòng)發(fā)起斷開(kāi)連接的一端(假定客戶(hù)端的套接字是綁定54305這個(gè)端口),那么最后發(fā)送ACK包的會(huì)是客戶(hù)端,如果這個(gè)ACK包丟包,服務(wù)端等待一段時(shí)間沒(méi)有收到這個(gè)ACK包會(huì)重新發(fā)FIN包給客戶(hù)端。

假如客戶(hù)端沒(méi)有time-wait狀態(tài)而是在發(fā)送ACK包后直接關(guān)閉連接(即刪除套接字,釋放54305這個(gè)端口),那么客戶(hù)端可能會(huì)為其他連接生成一個(gè)新的套接字綁定54305這個(gè)端口。服務(wù)端重發(fā)的FIN包到達(dá)客戶(hù)端后,客戶(hù)端根據(jù)包頭部的接收方端口找到了新的套接字上就會(huì)導(dǎo)致該新套接字上的連接關(guān)閉。

time-wait狀態(tài)一般會(huì)持續(xù)幾分鐘。

附:使用UDP協(xié)議收發(fā)包

相比于TCP協(xié)議,使用UDP協(xié)議發(fā)送包無(wú)需建立連接(通信雙方服務(wù)交換控制消息),因此數(shù)據(jù)的收發(fā)相比于TCP減小了開(kāi)銷(xiāo)和發(fā)送延遲更加高效。但是UDP協(xié)議沒(méi)有TCP協(xié)議的安全傳輸機(jī)制如確認(rèn)應(yīng)答機(jī)制,重傳和窗口等。

UDP頭部的控制信息:

UDP適用于以下場(chǎng)景:

a. 短數(shù)據(jù)

如果可以?xún)H用一個(gè)包就將所有數(shù)據(jù)發(fā)送給對(duì)端,那么就無(wú)需建立連接以保存雙方的IP和端口以及其他信息到套接字中。而且數(shù)據(jù)少意味著傳輸?shù)陌鼣?shù)量少,丟包的可能性就會(huì)減小,也就無(wú)需像TCP那樣對(duì)包的送達(dá)狀態(tài)進(jìn)行監(jiān)控。如果丟包,協(xié)議棧收不到對(duì)方的回復(fù)是不會(huì)自行重發(fā)數(shù)據(jù)包的,應(yīng)用程序可以自行組織重發(fā)數(shù)據(jù)(需要開(kāi)發(fā)應(yīng)用程序的人編寫(xiě)重試邏輯)。

b.視頻和音頻數(shù)據(jù)

音頻和視頻數(shù)據(jù)必須在規(guī)定的時(shí)間內(nèi)送達(dá),一旦送達(dá)晚了,就會(huì)錯(cuò)過(guò)播放時(shí)機(jī),導(dǎo)致聲音和圖像卡頓。如果像TCP一樣通過(guò)接收確認(rèn)應(yīng)答來(lái)檢查錯(cuò)誤并重發(fā),重發(fā)的過(guò)程需要消耗一定的時(shí)間,因此重發(fā)的數(shù)據(jù)很可能已經(jīng)錯(cuò)過(guò)了播放的時(shí)機(jī)。

此外,音頻和視頻數(shù)據(jù)中缺少了某些包并不會(huì)產(chǎn)生嚴(yán)重的問(wèn)題,只是會(huì)產(chǎn)生一些失真或者卡頓而已,一般都是可以接受的。使用UDP發(fā)送數(shù)據(jù)的效率會(huì)更高。

上面的操作都是圍繞著傳輸層TCP模塊介紹數(shù)據(jù)的收發(fā)。其實(shí)TCP報(bào)文還需要在網(wǎng)絡(luò)層的IP模塊中經(jīng)過(guò)添加IP頭部和MAC頭部封裝成包再委托給網(wǎng)卡發(fā)送出去。

下節(jié)預(yù)告:瀏覽器輸入一個(gè)網(wǎng)址發(fā)生了什么(三) IP模塊封裝、ARP協(xié)議、IP協(xié)議和網(wǎng)卡

責(zé)任編輯:趙寧寧 來(lái)源: 程序員阿沛
相關(guān)推薦

2024-11-04 10:00:00

瀏覽器網(wǎng)絡(luò)

2024-11-04 08:10:00

2024-11-22 16:20:28

2024-05-06 10:53:22

瀏覽器TCPHTTPS

2023-01-14 16:11:27

瀏覽器URL回車(chē)

2018-01-03 15:17:26

2020-09-01 11:40:01

HTTPJavaTCP

2022-03-04 08:56:58

HTTPDNS 服務(wù)器瀏覽器

2020-10-09 08:59:55

輸入網(wǎng)址解密

2024-04-11 08:33:25

2021-04-14 10:47:56

瀏覽器網(wǎng)址TCP

2025-06-30 09:26:47

2022-09-15 07:54:59

awaitPromise

2017-12-14 15:45:02

2009-05-27 08:54:15

瀏覽器平臺(tái)Chrome

2022-05-26 23:36:36

SQLMySQL數(shù)據(jù)

2021-06-02 06:14:50

Nyxt瀏覽器

2012-09-03 10:24:16

果粉瀏覽器

2017-04-26 14:15:35

瀏覽器緩存機(jī)制

2015-07-03 09:27:43

網(wǎng)絡(luò)閏秒
點(diǎn)贊
收藏

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