TCP 的那些事兒(上)

TCP是一個巨復(fù)雜的協(xié)議聋迎,因為他要解決很多問題姐帚,而這些問題又帶出了很多子問題和陰暗面衷旅。所以學(xué)習(xí)TCP本身是個比較痛苦的過程,但對于學(xué)習(xí)的過程卻能讓人有很多收獲课锌。關(guān)于TCP這個協(xié)議的細(xì)節(jié)厨内,我還是推薦你去看W.Richard Stevens的《TCP/IP 詳解 卷1:協(xié)議》(當(dāng)然祈秕,你也可以去讀一下RFC793以及后面N多的RFC)。另外雏胃,本文我會使用英文術(shù)語请毛,這樣方便你通過這些英文關(guān)鍵詞來查找相關(guān)的技術(shù)文檔。

之所以想寫這篇文章瞭亮,目的有三個方仿,

一個是想鍛煉一下自己是否可以用簡單的篇幅把這么復(fù)雜的TCP協(xié)議描清楚的能力。

另一個是覺得現(xiàn)在的好多程序員基本上不會認(rèn)認(rèn)真真地讀本書统翩,喜歡快餐文化仙蚜,所以,希望這篇快餐文章可以讓你對TCP這個古典技術(shù)有所了解唆缴,并能體會到軟件設(shè)計中的種種難處鳍征。并且你可以從中有一些軟件設(shè)計上的收獲黍翎。

最重要的希望這些基礎(chǔ)知識可以讓你搞清很多以前一些似是而非的東西面徽,并且你能意識到基礎(chǔ)的重要。

所以匣掸,本文不會面面俱到趟紊,只是對TCP協(xié)議、算法和原理的科普碰酝。

我本來只想寫一個篇幅的文章的霎匈,但是TCP真TMD的復(fù)雜,比C++復(fù)雜多了送爸,這30多年來铛嘱,各種優(yōu)化變種爭論和修改。所以袭厂,寫著寫著就發(fā)現(xiàn)只有砍成兩篇墨吓。

上篇中,主要向你介紹TCP協(xié)議的定義和丟包時的重傳機制纹磺。

下篇中帖烘,重點介紹TCP的流迭、擁塞處理橄杨。

廢話少說秘症,首先,我們需要知道TCP在網(wǎng)絡(luò)OSI的七層模型中的第四層——Transport層式矫,IP在第三層——Network層乡摹,ARP在第二層——Data Link層,在第二層上的數(shù)據(jù)采转,我們叫Frame聪廉,在第三層上的數(shù)據(jù)叫Packet,第四層的數(shù)據(jù)叫Segment。

首先锄列,我們需要知道图云,我們程序的數(shù)據(jù)首先會打到TCP的Segment中,然后TCP的Segment會打到IP的Packet中邻邮,然后再打到以太網(wǎng)Ethernet的Frame中竣况,傳到對端后,各個層解析自己的協(xié)議筒严,然后把數(shù)據(jù)交給更高層的協(xié)議處理丹泉。

TCP頭格式

接下來,我們來看一下TCP頭的格式

TCP頭格式

你需要注意這么幾點:

TCP的包是沒有IP地址的鸭蛙,那是IP層上的事摹恨。但是有源端口和目標(biāo)端口。

一個TCP連接需要四個元組來表示是同一個連接(src_ip, src_port, dst_ip, dst_port)準(zhǔn)確說是五元組娶视,還有一個是協(xié)議晒哄。但因為這里只是說TCP協(xié)議,所以肪获,這里我只說四元組寝凌。

注意上圖中的四個非常重要的東西:

Sequence Number是包的序號,用來解決網(wǎng)絡(luò)包亂序(reordering)問題孝赫。

Acknowledgement Number就是ACK——用于確認(rèn)收到较木,用來解決不丟包的問題

Window又叫Advertised-Window青柄,也就是著名的滑動窗口(Sliding Window)伐债,用于解決流控的

TCP Flag?致开,也就是包的類型峰锁,主要是用于操控TCP的狀態(tài)機的

關(guān)于其它的東西喇喉,可以參看下面的圖示


TCP/IP協(xié)議的詳細(xì)信息參看《TCP/IP協(xié)議詳解》三卷本祖今。下面是TCP報文格式圖:

TCP格式(中文版)

上圖中有幾個字段需要重點介紹下:

? (1)序號:Seq序號,占32位拣技,用來標(biāo)識從TCP源端向目的端發(fā)送的字節(jié)流千诬,發(fā)起方發(fā)送數(shù)據(jù)時對此進行標(biāo)記。

? (2)確認(rèn)序號:Ack序號膏斤,占32位徐绑,只有ACK標(biāo)志位為1時,確認(rèn)序號字段才有效莫辨,Ack=Seq+1傲茄。

? (3)標(biāo)志位:共6個毅访,即URG、ACK盘榨、PSH喻粹、RST、SYN草巡、FIN等守呜,具體含義如下:

? (A)URG:緊急指針(urgent pointer)有效。

? (B)ACK:確認(rèn)序號有效山憨。acknowledgment

? (C)PSH:接收方應(yīng)該盡快將這個報文交給應(yīng)用層查乒。

? (D)RST:重置連接。

? (E)SYN:發(fā)起一個新連接郁竟。synchronize

? (F)FIN:釋放一個連接玛迄。


TCP的狀態(tài)機

其實,網(wǎng)絡(luò)上的傳輸是沒有連接的棚亩,包括TCP也是一樣的蓖议。而TCP所謂的“連接”,其實只不過是在通訊的雙方維護一個“連接狀態(tài)”蔑舞,讓它看上去好像有連接一樣拒担。所以,TCP的狀態(tài)變換是非常重要的攻询。

下面是:“TCP協(xié)議的狀態(tài)機”(圖片來源) 和 “TCP建鏈接”、“TCP斷鏈接”州弟、“傳數(shù)據(jù)” 的對照圖钧栖,我把兩個圖并排放在一起,這樣方便在你對照著看婆翔。另外拯杠,下面這兩個圖非常非常的重要,你一定要記牢啃奴。(吐個槽:看到這樣復(fù)雜的狀態(tài)機潭陪,就知道這個協(xié)議有多復(fù)雜,復(fù)雜的東西總是有很多坑爹的事情最蕾,所以TCP協(xié)議其實也挺坑爹的)

很多人會問依溯,為什么建鏈接要3次握手,斷鏈接需要4次揮手瘟则?

對于建鏈接的3次握手黎炉,主要是要初始化Sequence Number 的初始值。通信的雙方要互相通知對方自己的初始化的Sequence Number(縮寫為ISN:Inital Sequence Number)——所以叫SYN醋拧,全稱Synchronize Sequence Numbers慷嗜。也就上圖中的 x 和 y淀弹。這個號要作為以后的數(shù)據(jù)通信的序號,以保證應(yīng)用層接收到的數(shù)據(jù)不會因為網(wǎng)絡(luò)上的傳輸?shù)膯栴}而亂序(TCP會用這個序號來拼接數(shù)據(jù))庆械。

對于4次揮手薇溃,其實你仔細(xì)看是2次,因為TCP是全雙工的缭乘,所以痊焊,發(fā)送方和接收方都需要Fin和Ack。只不過忿峻,有一方是被動的薄啥,所以看上去就成了所謂的4次揮手。如果兩邊同時斷連接逛尚,那就會就進入到CLOSING狀態(tài)垄惧,然后到達(dá)TIME_WAIT狀態(tài)。下圖是雙方同時斷連接的示意圖(你同樣可以對照著TCP狀態(tài)機看):

兩端同時斷連接


另外绰寞,有幾個事情需要注意一下:

關(guān)于建連接時SYN超時到逊。試想一下,如果server端接到了clien發(fā)的SYN后回了SYN-ACK后client掉線了滤钱,server端沒有收到client回來的ACK觉壶,那么,這個連接處于一個中間狀態(tài)件缸,即沒成功铜靶,也沒失敗。于是他炊,server端如果在一定時間內(nèi)沒有收到的TCP會重發(fā)SYN-ACK争剿。在Linux下,默認(rèn)重試次數(shù)為5次痊末,重試的間隔時間從1s開始每次都翻售蚕苇,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s凿叠,第5次發(fā)出后還要等32s都知道第5次也超時了涩笤,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s盒件,TCP才會把斷開這個連接蹬碧。

關(guān)于SYN Flood攻擊。一些惡意的人就為此制造了SYN Flood攻擊——給服務(wù)器發(fā)了一個SYN后履恩,就下線了锰茉,于是服務(wù)器需要默認(rèn)等63s才會斷開連接,這樣切心,攻擊者就可以把服務(wù)器的syn連接的隊列耗盡飒筑,讓正常的連接請求不能處理片吊。于是,Linux下給了一個叫tcp_syncookies的參數(shù)來應(yīng)對這個事——當(dāng)SYN隊列滿了后协屡,TCP會通過源地址端口俏脊、目標(biāo)地址端口和時間戳打造出一個特別的Sequence Number發(fā)回去(又叫cookie),如果是攻擊者則不會有響應(yīng)肤晓,如果是正常連接爷贫,則會把這個 SYN Cookie發(fā)回來,然后服務(wù)端可以通過cookie建連接(即使你不在SYN隊列中)补憾。請注意漫萄,請先千萬別用tcp_syncookies來處理正常的大負(fù)載的連接的情況。因為盈匾,synccookies是妥協(xié)版的TCP協(xié)議腾务,并不嚴(yán)謹(jǐn)。對于正常的請求削饵,你應(yīng)該調(diào)整三個TCP參數(shù)可供你選擇岩瘦,第一個是:tcp_synack_retries 可以用他來減少重試次數(shù);第二個是:tcp_max_syn_backlog窿撬,可以增大SYN連接數(shù)启昧;第三個是:tcp_abort_on_overflow 處理不過來干脆就直接拒絕連接了。

關(guān)于ISN的初始化劈伴。ISN是不能hard code的密末,不然會出問題的——比如:如果連接建好后始終用1來做ISN,如果client發(fā)了30個segment過去宰啦,但是網(wǎng)絡(luò)斷了苏遥,于是 client重連,又用了1做ISN赡模,但是之前連接的那些包到了,于是就被當(dāng)成了新連接的包师抄,此時,client的Sequence Number 可能是3,而Server端認(rèn)為client端的這個號是30了粘捎。全亂了昭抒。RFC793中說,ISN會和一個假的時鐘綁在一起茶鉴,這個時鐘會在每4微秒對ISN做加一操作锋玲,直到超過2^32,又從0開始涵叮。這樣惭蹂,一個ISN的周期大約是4.55個小時伞插。因為,我們假設(shè)我們的TCP Segment在網(wǎng)絡(luò)上的存活時間不會超過Maximum Segment Lifetime(縮寫為MSL –Wikipedia語條)盾碗,所以媚污,只要MSL的值小于4.55小時,那么廷雅,我們就不會重用到ISN耗美。

關(guān)于 MSL 和?TIME_WAIT。通過上面的ISN的描述航缀,相信你也知道MSL是怎么來的了商架。我們注意到,在TCP的狀態(tài)圖中芥玉,從TIME_WAIT狀態(tài)到CLOSED狀態(tài)蛇摸,有一個超時設(shè)置,這個超時設(shè)置是 2*MSL(RFC793定義了MSL為2分鐘飞傀,Linux設(shè)置成了30s)為什么要這有TIME_WAIT皇型?為什么不直接給轉(zhuǎn)成CLOSED狀態(tài)呢?主要有兩個原因:1)TIME_WAIT確保有足夠的時間讓對端收到了ACK砸烦,如果被動關(guān)閉的那方?jīng)]有收到Ack弃鸦,就會觸發(fā)被動端重發(fā)Fin,一來一去正好2個MSL幢痘,2)有足夠的時間讓這個連接不會跟后面的連接混在一起(你要知道唬格,有些自做主張的路由器會緩存IP數(shù)據(jù)包,如果連接被重用了颜说,那么這些延遲收到的包就有可能會跟新連接混在一起)。你可以看看這篇文章《TIME_WAIT and its design implications for protocols and scalable client server systems

關(guān)于TIME_WAIT數(shù)量太多喊积。從上面的描述我們可以知道玄妈,TIME_WAIT是個很重要的狀態(tài)乾吻,但是如果在大并發(fā)的短鏈接下绎签,TIME_WAIT 就會太多,這也會消耗很多系統(tǒng)資源酝锅。只要搜一下搔扁,你就會發(fā)現(xiàn),十有八九的處理方式都是教你設(shè)置兩個參數(shù)愉老,一個叫tcp_tw_reuse剖效,另一個叫tcp_tw_recycle的參數(shù)璧尸,這兩個參數(shù)默認(rèn)值都是被關(guān)閉的爷光,后者recyle比前者resue更為激進蛀序,resue要溫柔一些徐裸。另外重贺,如果使用tcp_tw_reuse气笙,必需設(shè)置tcp_timestamps=1潜圃,否則無效。這里蛉谜,你一定要注意,打開這兩個參數(shù)會有比較大的坑——可能會讓TCP連接出一些詭異的問題(因為如上述一樣,如果不等待超時重用連接的話赏廓,新的連接可能會建不上幔摸。正如官方文檔上說的一樣“It should not be changed without advice/request of technical experts”)既忆。

關(guān)于tcp_tw_reuse患雇。官方文檔上說tcp_tw_reuse 加上tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保證協(xié)議的角度上的安全苛吱,但是你需要tcp_timestamps在兩邊都被打開(你可以讀一下tcp_twsk_unique的源碼?)翠储。我個人估計還是有一些場景會有問題援所。

關(guān)于tcp_tw_recycle任斋。如果是tcp_tw_recycle被打開了話废酷,會假設(shè)對端開啟了tcp_timestamps澈蟆,然后會去比較時間戳趴俘,如果時間戳變大了寥闪,就可以重用疲憋。但是,如果對端是一個NAT網(wǎng)絡(luò)的話(如:一個公司只用一個IP出公網(wǎng))或是對端的IP被另一臺重用了,這個事就復(fù)雜了构舟。建鏈接的SYN可能就被直接丟掉了(你可能會看到connection time out的錯誤)(如果你想觀摩一下Linux的內(nèi)核代碼谐檀,請參看源碼?tcp_timewait_state_process)溃肪。

關(guān)于tcp_max_tw_buckets。這個是控制并發(fā)的TIME_WAIT的數(shù)量,默認(rèn)值是180000,如果超限,那么,系統(tǒng)會把多的給destory掉谊惭,然后在日志里打一個警告(如:time wait bucket table overflow)药磺,官網(wǎng)文檔說這個參數(shù)是用來對抗DDoS攻擊的木缝。也說的默認(rèn)值180000并不小矫俺。這個還是需要根據(jù)實際情況考慮稿湿。

Again包斑,使用tcp_tw_reuse和tcp_tw_recycle來解決TIME_WAIT的問題是非常非常危險的丸卷,因為這兩個參數(shù)違反了TCP協(xié)議(RFC?1122

其實凹联,TIME_WAIT表示的是你主動斷連接,所以,這就是所謂的“不作死不會死”蚌铜。試想审葬,如果讓對端斷連接,那么這個破問題就是對方的了,呵呵昆汹。另外辈末,如果你的服務(wù)器是于HTTP服務(wù)器,那么設(shè)置一個HTTP的KeepAlive有多重要(瀏覽器會重用一個TCP連接來處理多個HTTP請求)组去,然后讓客戶端去斷鏈接(你要小心,瀏覽器可能會非常貪婪寿烟,他們不到萬不得已不會主動斷連接)。

數(shù)據(jù)傳輸中的Sequence Number

下圖是我從Wireshark中截了個我在訪問coolshell.cn時的有數(shù)據(jù)傳輸?shù)膱D給你看一下氏淑,SeqNum是怎么變的假残。(使用Wireshark菜單中的Statistics ->Flow Graph… )

你可以看到眶俩,SeqNum的增加是和傳輸?shù)淖止?jié)數(shù)相關(guān)的。上圖中,三次握手后燃乍,來了兩個Len:1440的包夯秃,而第二個包的SeqNum就成了1441介陶。然后第一個ACK回的是1441舌缤,表示第一個1440收到了国撵。

注意:如果你用Wireshark抓包程序看3次握手,你會發(fā)現(xiàn)SeqNum總是為0环础,不是這樣的线得,Wireshark為了顯示更友好,使用了Relative SeqNum——相對序號魏保,你只要在右鍵菜單中的protocol preference 中取消掉就可以看到“Absolute SeqNum”了

TCP重傳機制

TCP要保證所有的數(shù)據(jù)包都可以到達(dá),所以檩咱,必需要有重傳機制刻蚯。

注意,接收端給發(fā)送端的Ack確認(rèn)只會確認(rèn)最后一個連續(xù)的包讨便,比如伴找,發(fā)送端發(fā)了1,2,3,4,5一共五份數(shù)據(jù)技矮,接收端收到了1,2耿币,于是回ack 3,然后收到了4(注意此時3沒收到),此時的TCP會怎么辦楷掉?我們要知道烹植,因為正如前面所說的,SeqNum和Ack是以字節(jié)數(shù)為單位墩虹,所以ack的時候,不能跳著確認(rèn)菌湃,只能確認(rèn)最大的連續(xù)收到的包惧所,不然或油,發(fā)送端就以為之前的都收到了顶岸。

超時重傳機制

一種是不回ack,死等3卷谈,當(dāng)發(fā)送方發(fā)現(xiàn)收不到3的ack超時后,會重傳3污淋。一旦接收方收到3后,會ack 回 4——意味著3和4都收到了赁豆。

但是,這種方式會有比較嚴(yán)重的問題务嫡,那就是因為要死等3心铃,所以會導(dǎo)致4和5即便已經(jīng)收到了,而發(fā)送方也完全不知道發(fā)生了什么事愉棱,因為沒有收到Ack艾岂,所以王浴,發(fā)送方可能會悲觀地認(rèn)為也丟了,所以有可能也會導(dǎo)致4和5的重傳钞啸。

對此有兩種選擇:

一種是僅重傳timeout的包。也就是第3份數(shù)據(jù)硕勿。

另一種是重傳timeout后所有的數(shù)據(jù)扼褪,也就是第3脏毯,4食店,5這三份數(shù)據(jù)。

這兩種方式有好也有不好自娩。第一種會節(jié)省帶寬脐彩,但是慢,第二種會快一點佛南,但是會浪費帶寬,也可能會有無用功妈拌。但總體來說都不好。因為都在等timeout培愁,timeout可能會很長(在下篇會說TCP是怎么動態(tài)地計算出timeout的)

快速重傳機制

于是,TCP引入了一種叫Fast Retransmit?的算法私股,不以時間驅(qū)動,而以數(shù)據(jù)驅(qū)動重傳。也就是說优床,如果,包沒有連續(xù)到達(dá)竿秆,就ack最后那個可能被丟了的包歉备,如果發(fā)送方連續(xù)收到3次相同的ack蕾羊,就重傳。Fast Retransmit的好處是不用等timeout了再重傳。

比如:如果發(fā)送方發(fā)出了1哀澈,2割按,3,4磷籍,5份數(shù)據(jù)适荣,第一份先到送了,于是就ack回2院领,結(jié)果2因為某些原因沒收到,3到達(dá)了栅盲,于是還是ack回2,后面的4和5都到了废恋,但是還是ack回2谈秫,因為2還是沒有收到,于是發(fā)送端收到了三個ack=2的確認(rèn)鱼鼓,知道了2還沒有到拟烫,于是就馬上重轉(zhuǎn)2。然后迄本,接收端收到了2硕淑,此時因為3,4,5都收到了置媳,于是ack回6于樟。示意圖如下:

Fast?Retransmit只解決了一個問題,就是timeout的問題拇囊,它依然面臨一個艱難的選擇迂曲,就是,是重傳之前的一個還是重傳所有的問題寥袭。對于上面的示例來說路捧,是重傳#2呢還是重傳#2,#3传黄,#4杰扫,#5呢?因為發(fā)送端并不清楚這連續(xù)的3個ack(2)是誰傳回來的膘掰?也許發(fā)送端發(fā)了20份數(shù)據(jù)章姓,是#6,#10炭序,#20傳來的呢啤覆。這樣,發(fā)送端很有可能要重傳從2到20的這堆數(shù)據(jù)(這就是某些TCP的實際的實現(xiàn))惭聂〈吧可見,這是一把雙刃劍辜纲。

SACK 方法

另外一種更好的方式叫:Selective Acknowledgment (SACK)(參看RFC 2018)笨觅,這種方式需要在TCP頭里加一個SACK的東西,ACK還是Fast Retransmit的ACK耕腾,SACK則是匯報收到的數(shù)據(jù)碎版见剩。參看下圖:

這樣,在發(fā)送端就可以根據(jù)回傳的SACK來知道哪些數(shù)據(jù)到了扫俺,哪些沒有到苍苞。于是就優(yōu)化了Fast?Retransmit的算法。當(dāng)然狼纬,這個協(xié)議需要兩邊都支持羹呵。在 Linux下,可以通過tcp_sack參數(shù)打開這個功能(Linux 2.4后默認(rèn)打開)疗琉。

這里還需要注意一個問題——接收方Reneging冈欢,所謂Reneging的意思就是接收方有權(quán)把已經(jīng)報給發(fā)送端SACK里的數(shù)據(jù)給丟了。這樣干是不被鼓勵的盈简,因為這個事會把問題復(fù)雜化了凑耻,但是太示,接收方這么做可能會有些極端情況,比如要把內(nèi)存給別的更重要的東西香浩。所以类缤,發(fā)送方也不能完全依賴SACK,還是要依賴ACK弃衍,并維護Time-Out呀非,如果后續(xù)的ACK沒有增長,那么還是要把SACK的東西重傳镜盯,另外岸裙,接收端這邊永遠(yuǎn)不能把SACK的包標(biāo)記為Ack。

注意:SACK會消費發(fā)送方的資源速缆,試想降允,如果一個攻擊者給數(shù)據(jù)發(fā)送方發(fā)一堆SACK的選項,這會導(dǎo)致發(fā)送方開始要重傳甚至遍歷已經(jīng)發(fā)出的數(shù)據(jù)艺糜,這會消耗很多發(fā)送端的資源剧董。詳細(xì)的東西請參看《TCP SACK的性能權(quán)衡

Duplicate SACK – 重復(fù)收到數(shù)據(jù)的問題

Duplicate SACK又稱D-SACK,其主要使用了SACK來告訴發(fā)送方有哪些數(shù)據(jù)被重復(fù)接收了破停。RFC-2883?里有詳細(xì)描述和示例翅楼。下面舉幾個例子(來源于RFC-2883

D-SACK使用了SACK的第一個段來做標(biāo)志,

如果SACK的第一個段的范圍被ACK所覆蓋真慢,那么就是D-SACK

如果SACK的第一個段的范圍被SACK的第二個段覆蓋毅臊,那么就是D-SACK

示例一:ACK丟包

下面的示例中,丟了兩個ACK黑界,所以管嬉,發(fā)送端重傳了第一個數(shù)據(jù)包(3000-3499),于是接收端發(fā)現(xiàn)重復(fù)收到朗鸠,于是回了一個SACK=3000-3500蚯撩,因為ACK都到了4000意味著收到了4000之前的所有數(shù)據(jù),所以這個SACK就是D-SACK——旨在告訴發(fā)送端我收到了重復(fù)的數(shù)據(jù)烛占,而且我們的發(fā)送端還知道胎挎,數(shù)據(jù)包沒有丟,丟的是ACK包忆家。

Transmitted? Received??? ACK Sent

Segment????? Segment???? (Including SACK Blocks)

3000-3499??? 3000-3499?? 3500 (ACK dropped)

3500-3999??? 3500-3999?? 4000 (ACK dropped)

3000-3499??? 3000-3499?? 4000, SACK=3000-3500

????????????????????????????????????---------

?示例二犹菇,網(wǎng)絡(luò)延誤

下面的示例中,網(wǎng)絡(luò)包(1000-1499)被網(wǎng)絡(luò)給延誤了弦赖,導(dǎo)致發(fā)送方?jīng)]有收到ACK,而后面到達(dá)的三個包觸發(fā)了“Fast Retransmit算法”浦辨,所以重傳蹬竖,但重傳時沼沈,被延誤的包又到了,所以币厕,回了一個SACK=1000-1500列另,因為ACK已到了3000,所以旦装,這個SACK是D-SACK——標(biāo)識收到了重復(fù)的包页衙。

這個案例下,發(fā)送端知道之前因為“Fast Retransmit算法”觸發(fā)的重傳不是因為發(fā)出去的包丟了阴绢,也不是因為回應(yīng)的ACK包丟了店乐,而是因為網(wǎng)絡(luò)延時了。

Transmitted??? Received??? ACK Sent

Segment??????? Segment???? (Including SACK Blocks)

500-999??????? 500-999???? 1000

1000-1499????? (delayed)

1500-1999????? 1500-1999?? 1000, SACK=1500-2000

2000-2499????? 2000-2499?? 1000, SACK=1500-2500

2500-2999????? 2500-2999?? 1000, SACK=1500-3000

1000-1499????? 1000-1499?? 3000

???????????????1000-1499?? 3000, SACK=1000-1500

??????????????????????????????????????---------

可見呻袭,引入了D-SACK眨八,有這么幾個好處:

1)可以讓發(fā)送方知道,是發(fā)出去的包丟了左电,還是回來的ACK包丟了廉侧。

2)是不是自己的timeout太小了,導(dǎo)致重傳篓足。

3)網(wǎng)絡(luò)上出現(xiàn)了先發(fā)的包后到的情況(又稱reordering)

4)網(wǎng)絡(luò)上是不是把我的數(shù)據(jù)包給復(fù)制了段誊。

知道這些東西可以很好得幫助TCP了解網(wǎng)絡(luò)情況,從而可以更好的做網(wǎng)絡(luò)上的流控栈拖。

Linux下的tcp_dsack參數(shù)用于開啟這個功能(Linux 2.4后默認(rèn)打開)

好了连舍,上篇就到這里結(jié)束了。如果你覺得我寫得還比較淺顯易懂辱魁,那么烟瞧,歡迎移步看下篇《TCP的那些事(下)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市染簇,隨后出現(xiàn)的幾起案子参滴,更是在濱河造成了極大的恐慌,老刑警劉巖锻弓,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砾赔,死亡現(xiàn)場離奇詭異,居然都是意外死亡青灼,警方通過查閱死者的電腦和手機暴心,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杂拨,“玉大人专普,你說我怎么就攤上這事〉粒” “怎么了檀夹?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵筋粗,是天一觀的道長。 經(jīng)常有香客問我炸渡,道長娜亿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任蚌堵,我火速辦了婚禮买决,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吼畏。我一直安慰自己督赤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布宫仗。 她就那樣靜靜地躺著够挂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藕夫。 梳的紋絲不亂的頭發(fā)上孽糖,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音毅贮,去河邊找鬼办悟。 笑死,一個胖子當(dāng)著我的面吹牛滩褥,可吹牛的內(nèi)容都是我干的病蛉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼瑰煎,長吁一口氣:“原來是場噩夢啊……” “哼铺然!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酒甸,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤裙品,失蹤者是張志新(化名)和其女友劉穎拭荤,沒想到半個月后迄薄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體然痊,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年农尖,在試婚紗的時候發(fā)現(xiàn)自己被綠了析恋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡盛卡,死狀恐怖助隧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滑沧,我是刑警寧澤并村,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布漏健,位于F島的核電站,受9級特大地震影響橘霎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殖属,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一姐叁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洗显,春花似錦外潜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玄组,卻和暖如春滔驾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俄讹。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工哆致, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人患膛。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓摊阀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親踪蹬。 傳聞我的和親對象是個殘疾皇子胞此,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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

  • TCP是一個巨復(fù)雜的協(xié)議,因為他要解決很多問題跃捣,而這些問題又帶出了很多子問題和陰暗面漱牵。所以學(xué)習(xí)TCP本身是個比較痛...
    愛我你就抱抱我閱讀 305評論 0 1
  • 本文主要通過整理網(wǎng)絡(luò)上的資料,整理出的關(guān)于TCP方面的簡單理論知識枝缔。作為Java程序員雖然更多的時候我們都是直接調(diào)...
    tomas家的小撥浪鼓閱讀 5,550評論 1 100
  • 個人認(rèn)為布疙,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,060評論 0 8
  • 預(yù)備知識 TCP 頭格式 截圖來源 TCP的包是沒有IP地址的愿卸,那是IP層上的事灵临。但是有源端口和目標(biāo)端口。一個TC...
    vedon_fu閱讀 800評論 0 2
  • 套接字選項SO_RESUEADDR 即使端口處于2MSL狀態(tài)趴荸,使用該選項儒溉,仍然能夠在該端口建立連接。服務(wù)器常會設(shè)置...
    Myth52125閱讀 1,411評論 0 0