什么是TCP序列號(hào)(Sequence number)?它是如何用于可靠的按序傳遞的?

? ? ? ?這個(gè)文章專門用于回答熱心好學(xué)同學(xué)的問題,我非常贊賞這樣打破砂鍋問到底的老鐵的學(xué)習(xí)精神,@lex都原諒是屑差點(diǎn)問到我的知識(shí)死角,因?yàn)橐话闱闆r下我們是不會(huì)關(guān)心TCP協(xié)議的每個(gè)細(xì)節(jié)。(評(píng)論在這里)。
先把TCP段的標(biāo)頭(首部)格式圖貼出來

? ? ? ?標(biāo)頭中,Sequence number(Seq)有32bit,即4個(gè)字節(jié),無論有沒有攜帶數(shù)據(jù)負(fù)載這個(gè)都是必須傳遞的,TCP主要是用Seq跟蹤數(shù)據(jù)段傳輸數(shù)據(jù)的字節(jié)范圍,以確??梢詸z測丟失和傳遞數(shù)據(jù)包的順序,如果數(shù)據(jù)丟失或者無序的到達(dá)目的地,TCP會(huì)嘗試重傳或者重新根據(jù)Seq恢復(fù)原始順序。
? ? ? ?TCP序列號(hào)的計(jì)算方法先給個(gè)輪廓,假如傳出的Seq是x,tcp數(shù)據(jù)段的長度為y,如果成功到達(dá)另一端,則下一個(gè)傳出的TCP數(shù)據(jù)段的序列號(hào)就是x+y。詳細(xì)例子繼續(xù)往下看。
? ? ? ?標(biāo)頭中,Acknowledgment number(Ack)8個(gè)字節(jié),它的大小表示我期望對(duì)方下次你的Seq從這個(gè)地方開始,并確認(rèn)我已經(jīng)接受到了之前的數(shù)據(jù)。
? ? ? ?標(biāo)頭中 Data offset 4位,最大能表示整數(shù)15,它是以32位字即 32/8=4字節(jié)為單位,表示整個(gè)header(標(biāo)頭/首部)的大小,也用于計(jì)算TCP數(shù)據(jù)包從那個(gè)字節(jié)位置開始屬于真正的數(shù)據(jù)負(fù)載(實(shí)際攜帶數(shù)據(jù))部分。

? ? ? ?TCP是雙工操作的,意思就是Seq由客戶端和服務(wù)器端建立連接的時(shí)候決定來初始序列號(hào),我們來看下三次握手建立連接協(xié)商序列號(hào)的抓包圖:

上交互過程:(藍(lán)色代表跟客戶的Seq有關(guān)系,紅色代表跟服務(wù)器有關(guān)系)
客戶端--------[SYN]? Seq=0 ---------------->服務(wù)器
客戶端<------[SYN,ACK]??Seq=0 Ack=1-----服務(wù)器
客戶端--------[ACK]??Seq=1? Ack=1-------->服務(wù)器
解釋:
第一次握手客戶端發(fā)送 seq=0,告訴服務(wù)器建立連接后“我可以從0計(jì)算序列號(hào)開始傳遞數(shù)據(jù)”;
第二次握手服務(wù)器回復(fù)seq=0,ack=1表示建立連接后“我也可以從0開始計(jì)算序列號(hào)傳遞數(shù)據(jù),但我想讓你從1開始計(jì)算序列號(hào)”。
第三次握手客戶端回復(fù)seq=1,ack=1,表示我們干脆都從1開始計(jì)算吧。

? ? ? ?接下來我們看看請(qǐng)求數(shù)據(jù)的過程,這個(gè)序號(hào)是咋來的,就能搞明白TCP到底是怎么處理數(shù)據(jù)包的。先看按照http1.1協(xié)議請(qǐng)求一個(gè)3928字節(jié)的CSS文件看看過程(注意下圖的紅色框和紅色字部分)。

第一個(gè)請(qǐng)求交互過程:
客戶端--------[SYN]??Seq=1 Ack=1 Len=368?---------------->服務(wù)器
解釋:
? ? ? ? 客戶端發(fā)送請(qǐng)求要一個(gè)css文件,從協(xié)商的seq=1開始,以及確認(rèn)的服務(wù)器給數(shù)據(jù)需要開始的位置Ack=1,抓包工具還給了個(gè)Len=368,代表客戶端請(qǐng)求的TCP數(shù)據(jù)包大小是368字節(jié)。
? ? ? ? 我去,這個(gè)Len(TCP Segment Len)的大小哪來的,TCP Header里沒定義這個(gè)幀頭字段啊?!這就是我為啥上圖中點(diǎn)開了網(wǎng)絡(luò)層IP header部分的原因(注意紅框),IP協(xié)議里有IP協(xié)議包大小的字段Total Length(這里是408字節(jié)),IP的header(首部)大小說20字節(jié),TCP的頭部大小這次請(qǐng)求中也是20字節(jié),那么 408-20-20=368字節(jié),那么到了目的地,攜帶的數(shù)據(jù)大小不就有了嘛!

接下來按照開始說的Seq的計(jì)算方式,我們根據(jù)圖看看剩下的交互部分:

按上圖中手寫的數(shù)字順序,是整個(gè)css文件完成傳輸?shù)倪^程。css文件交互完成總共六步:
交互過程:
客戶端--------[PSH ACK]??Seq=1?Ack=1 Len=368-------->服務(wù)器
客戶端<------[ACK]??Seq=1?Ack=369 Len=1412------------服務(wù)器
客戶端--------[ACK]??Seq=369??Ack=1413?Len=0?--------->服務(wù)器
客戶端<------[ACK]??Seq=1413?Ack=369?Len=1412--------服務(wù)器
客戶端<------[PSH ACK]??Seq=2825?Ack=369?Len=1104---服務(wù)器
客戶端--------[ACK]??Seq=369??Ack=3929?Len=0?---------->服務(wù)器
解釋:
客戶端發(fā)送請(qǐng)求,帶上了標(biāo)職位PSH,它代表直接將緩沖區(qū)的數(shù)據(jù)推送給接收方,一般沒有特別意義,所以目前請(qǐng)求和響應(yīng)最后一次的數(shù)據(jù)會(huì)加入PSH標(biāo)志位;以及帶上了數(shù)據(jù)負(fù)載大小為368字節(jié)。
服務(wù)器則直接Ack=368+1=369代表數(shù)據(jù)接受成功,并帶上分割數(shù)據(jù)包第一部分1412自己發(fā)送給客戶端。
客戶端收到后,直接響應(yīng)服務(wù)器確認(rèn)的字節(jié)為它的Seq=369,因?yàn)樗_認(rèn)沒攜帶多余的數(shù)據(jù),并確認(rèn)我收到了數(shù)據(jù),給服務(wù)器說你可以從1413繼續(xù)回復(fù)給我。
服務(wù)器繼續(xù)攜帶第二部分的TCP數(shù)據(jù)包,攜帶1412自己的數(shù)據(jù),并將Seq=1413,告訴客戶的,我按你期望的順序,從1413字節(jié)開始再發(fā)給你1412字節(jié)的數(shù)據(jù)。
服務(wù)器發(fā)現(xiàn)剩下最后一部分1104字節(jié)的數(shù)據(jù)了,不等客戶端確認(rèn),直接推送給客戶的,并設(shè)置Seq=2825.
客戶端回復(fù)Ack=3929(第四步seq1413+第四步Len1412+第五步Len1104),告訴服務(wù)器第四第五步給我的數(shù)據(jù)都收到 。
最后總結(jié)看下圖:表示這次請(qǐng)求分成了個(gè)TCP數(shù)據(jù)包,每個(gè)包的數(shù)據(jù)負(fù)載從x字節(jié)到x自己的數(shù)據(jù)范圍,重新組裝的數(shù)據(jù)大小是3928字節(jié)。省下就交給應(yīng)用程序?qū)犹幚砹耍?br>


? ? ? ? 最后再補(bǔ)充點(diǎn)TCP數(shù)據(jù)包不可能大于1460字節(jié)的知識(shí),因?yàn)閿?shù)據(jù)到達(dá)下一層網(wǎng)絡(luò)層,數(shù)據(jù)超過1500字節(jié)會(huì)被網(wǎng)絡(luò)層再次分包,一般TCP數(shù)據(jù)包的分割最大大小(MSS-maximun segment size)可能就是1500字節(jié)-IP頭20字節(jié)-TCP頭最少20字節(jié)=1460字節(jié),就能保證TCP的數(shù)據(jù)包不需要下一層網(wǎng)絡(luò)層做分包的工作了,避免分包錯(cuò)誤丟棄數(shù)據(jù)的風(fēng)險(xiǎn)。其實(shí)在建立連接三次握手的時(shí)候,雙方已經(jīng)協(xié)商了MSS的值了,就藏在標(biāo)頭option字段里,見下圖。

? ?講完了,不明白的歡迎留言評(píng)論??