dede可以做視頻網(wǎng)站百度關(guān)鍵詞指數(shù)排行
目錄
1. 流量控制
2. 滑動(dòng)窗口
3. 擁塞控制
4. 延遲應(yīng)答
5. 捎帶應(yīng)答
6. 面向字節(jié)流
7. 粘包問(wèn)題
8. TCP異常情況
1. 流量控制
- 接收端將自己可以接收的緩沖區(qū)大小放入 TCP 首部中的 "窗口大小" 字段, 通過(guò)ACK端通知發(fā)送端;
- 窗口大小字段越大, 說(shuō)明網(wǎng)絡(luò)的吞吐量越高;
- 接收端一旦發(fā)現(xiàn)自己的緩沖區(qū)快滿了, 就會(huì)將窗口大小設(shè)置成一個(gè)更小的值通知給發(fā)送端;
- 發(fā)送端接受到這個(gè)窗口之后, 就會(huì)減慢自己的發(fā)送速度;
- 如果接收端緩沖區(qū)滿了, 就會(huì)將窗口置為0; 這時(shí)發(fā)送方不再發(fā)送數(shù)據(jù), 但是需要定期發(fā)送一個(gè)窗口探測(cè)數(shù)據(jù)段, 使接收端把窗口大小告訴發(fā)送端。
- 第一次發(fā)送數(shù)據(jù)時(shí)的窗口大小,是在三次握手時(shí)交換的。
2. 滑動(dòng)窗口
? ? ? ? 在上一篇文章中介紹了ACK確認(rèn)應(yīng)答和超時(shí)重傳機(jī)制,那么根據(jù)前面已有的知識(shí),如果發(fā)送數(shù)據(jù)在沒(méi)有收到應(yīng)答之前,必須將已經(jīng)發(fā)送的數(shù)據(jù)暫時(shí)保留,用來(lái)在沒(méi)有收到確認(rèn)ACK時(shí)實(shí)現(xiàn)超時(shí)重傳機(jī)制;會(huì)保存在滑動(dòng)窗口中。若是收到ACK后再發(fā)送下一個(gè)數(shù)據(jù)段,這樣做有一個(gè)比較大的缺點(diǎn), 就是性能較差. 尤其是數(shù)據(jù)往返的時(shí)間較長(zhǎng)的時(shí)候
為了使得傳輸效率變得更高,可以有下圖中的傳輸策略:
- 窗口大小指的是無(wú)需等待確認(rèn)應(yīng)答而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值. 上圖的窗口大小就是4000個(gè)字節(jié)(四個(gè)段).
- 發(fā)送前四個(gè)段的時(shí)候, 不需要等待任何ACK, 直接發(fā)送
- 收到第一個(gè)ACK后, 滑動(dòng)窗口向后移動(dòng), 繼續(xù)發(fā)送第五個(gè)段的數(shù)據(jù); 依次類推;
- 操作系統(tǒng)內(nèi)核為了維護(hù)這個(gè)滑動(dòng)窗口, 需要開(kāi)辟 發(fā)送緩沖區(qū) 來(lái)記錄當(dāng)前還有哪些數(shù)據(jù)沒(méi)有應(yīng)答; 只有確認(rèn)應(yīng)答過(guò)的數(shù)據(jù), 才能從緩沖區(qū)刪掉;
- 窗口越大, 則網(wǎng)絡(luò)的吞吐率就越高;

?不過(guò)窗口不是一定會(huì)向右滑動(dòng)的:
- 窗口保持不變:發(fā)送的數(shù)據(jù)速度與接收到ACK的速度相當(dāng)時(shí)
- 窗口變小:對(duì)方接收緩沖區(qū)滿了,窗口end端不在變化,且一直收到對(duì)方的ACK,start端不斷靠近end端
- 窗口變大:由于網(wǎng)絡(luò)擁堵等的情況一直未收到對(duì)方的ACK,start端不變化或者速度較慢;而對(duì)方接受緩沖區(qū)在變大,一直發(fā)送新的數(shù)據(jù),窗口end端一直向右移動(dòng)。
若發(fā)生丟包情況怎么辦呢?
情況一:數(shù)據(jù)沒(méi)丟,只是應(yīng)答丟了 -- 這種情況是無(wú)需擔(dān)心的,因?yàn)榭梢酝ㄟ^(guò)后續(xù)的ACK進(jìn)行確認(rèn)。發(fā)送端在收到下一個(gè)應(yīng)答的序號(hào)為:ack_seq = x+1,表示x+1之前的數(shù)據(jù)已經(jīng)全部收到了,如下圖所示:
情況二:數(shù)據(jù)包真的丟失了
- 當(dāng)某一段報(bào)文段丟失之后, 發(fā)送端會(huì)一直收到 1001 這樣的ACK, 就像是在提醒發(fā)送端 "我想要的是 1001"一樣;
- 如果發(fā)送端主機(jī)連續(xù)三次收到了同樣一個(gè) "1001" 這樣的應(yīng)答, 就會(huì)將對(duì)應(yīng)的數(shù)據(jù) 1001 - 2000 重新發(fā)送;
- 這個(gè)時(shí)候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因?yàn)?001 - 7000)接收端其實(shí)之前就已經(jīng)收到了, 被放到了接收端操作系統(tǒng)內(nèi)核的接收緩沖區(qū)中
這種機(jī)制叫做“高速重發(fā)控制”(快重傳),比超時(shí)重發(fā)機(jī)制要快。
3. 擁塞控制
- 此處引入一個(gè)概念程為擁塞窗口
- 發(fā)送開(kāi)始的時(shí)候, 定義擁塞窗口大小為1;
- 每次收到一個(gè)ACK應(yīng)答, 擁塞窗口加1;
- 每次發(fā)送數(shù)據(jù)包的時(shí)候, 將擁塞窗口和接收端主機(jī)反饋的窗口大小做比較, 取較小的值作為實(shí)際發(fā)送的窗口;
慢啟動(dòng)只是啟動(dòng)的時(shí)候慢,但是是指數(shù)級(jí)的增長(zhǎng)速度。為了不增長(zhǎng)的那么快, 因此不能使擁塞窗口單純的加倍。此處引入一個(gè)叫做慢啟動(dòng)的閾值,當(dāng)擁塞窗口超過(guò)這個(gè)閾值的時(shí)候, 不再按照指數(shù)方式增長(zhǎng), 而是按照線性方式增長(zhǎng)。如下圖所示:
4. 延遲應(yīng)答
- 假設(shè)接收端緩沖區(qū)為1M. 一次收到了500K的數(shù)據(jù); 如果立刻應(yīng)答, 返回的窗口就是500K;
- 但實(shí)際上可能處理端處理的速度很快, 10ms之內(nèi)就把500K數(shù)據(jù)從緩沖區(qū)消費(fèi)掉了;
- 在這種情況下, 接收端處理還遠(yuǎn)沒(méi)有達(dá)到自己的極限, 即使窗口再放大一些, 也能處理過(guò)來(lái);
- 如果接收端稍微等一會(huì)再應(yīng)答, 比如等待200ms再應(yīng)答, 那么這個(gè)時(shí)候返回的窗口大小就是1M;
并不是所有的包都要延遲應(yīng)答:
- 數(shù)量限制: 每隔N個(gè)包就應(yīng)答一次; (N一般為2)
- 時(shí)間限制: 超過(guò)最大延遲時(shí)間就應(yīng)答一次? (超時(shí)時(shí)間取200ms)
延遲應(yīng)答不僅可以返回簡(jiǎn)答的窗口大小,?還可以:
- 合并ACK:如果連續(xù)收到兩個(gè)TCP包,并不一定需要ACK兩次,只要回復(fù)最終的ACK就可以了,可以降低網(wǎng)絡(luò)流量。
- 如果接收方有數(shù)據(jù)要發(fā)送,那么就會(huì)在發(fā)送數(shù)據(jù)的TCP數(shù)據(jù)包里,帶上ACK信息。這樣做,可以避免大量的ACK以一個(gè)單獨(dú)的TCP包發(fā)送,減少了網(wǎng)絡(luò)流量
5. 捎帶應(yīng)答
6. 面向字節(jié)流
? ? ? ??當(dāng)用戶消息通過(guò) TCP 協(xié)議傳輸時(shí),消息可能會(huì)被操作系統(tǒng)分組成多個(gè)的 TCP 報(bào)文,也就是一個(gè)完整的用戶消息被拆分成多個(gè) TCP 報(bào)文進(jìn)行傳輸。
創(chuàng)建一個(gè)TCP的socket, 同時(shí)在內(nèi)核中創(chuàng)建一個(gè) 發(fā)送緩沖區(qū) 和一個(gè) 接收緩沖區(qū);
- 調(diào)用write時(shí), 數(shù)據(jù)會(huì)先寫入發(fā)送緩沖區(qū)中;
- 如果發(fā)送的字節(jié)數(shù)太長(zhǎng), 會(huì)被拆分成多個(gè)TCP的數(shù)據(jù)包發(fā)出;
- 如果發(fā)送的字節(jié)數(shù)太短, 就會(huì)先在緩沖區(qū)里等待, 等到緩沖區(qū)長(zhǎng)度差不多了, 或者其他合適的時(shí)機(jī)發(fā)送出去;
- 接收數(shù)據(jù)的時(shí)候, 數(shù)據(jù)也是從網(wǎng)卡驅(qū)動(dòng)程序到達(dá)內(nèi)核的接收緩沖區(qū);
- 然后應(yīng)用程序可以調(diào)用read從接收緩沖區(qū)拿數(shù)據(jù);
- 另一方面, TCP的一個(gè)連接, 既有發(fā)送緩沖區(qū), 也有接收緩沖區(qū), 那么對(duì)于這一個(gè)連接, 既可以讀數(shù)據(jù), 也可以寫數(shù)據(jù). 這個(gè)概念叫做 全雙工
由于緩沖區(qū)的存在, TCP程序的讀和寫不需要一一匹配, 例如:
- 寫100個(gè)字節(jié)數(shù)據(jù)時(shí), 可以調(diào)用一次write寫100個(gè)字節(jié), 也可以調(diào)用100次write, 每次寫一個(gè)字節(jié);
- 讀100個(gè)字節(jié)數(shù)據(jù)時(shí), 也完全不需要考慮寫的時(shí)候是怎么寫的, 既可以一次read 100個(gè)字節(jié), 也可以一次read一個(gè)字節(jié), 重復(fù)100次;
7. 粘包問(wèn)題
如果一次請(qǐng)求發(fā)送的數(shù)據(jù)量比較小,沒(méi)達(dá)到緩沖區(qū)大小,TCP則會(huì)將多個(gè)請(qǐng)求合并為同一個(gè)請(qǐng)求進(jìn)行發(fā)送,這就形成了粘包問(wèn)題;
如果一次請(qǐng)求發(fā)送的數(shù)據(jù)量比較大,超過(guò)了緩沖區(qū)大小,TCP就會(huì)將其拆分為多次發(fā)送,這就是拆包,也就是將一個(gè)大的包拆分為多個(gè)小包進(jìn)行發(fā)送。
那么如何避免粘包問(wèn)題呢? 歸根結(jié)底就是一句話, 明確兩個(gè)包之間的邊界
- 對(duì)于定長(zhǎng)的包, 保證每次都按固定大小讀取即可; 例如上面的Request結(jié)構(gòu), 是固定大小的, 那么就從緩沖區(qū)從頭開(kāi)始按sizeof(Request)依次讀取即可;
- 對(duì)于變長(zhǎng)的包, 可以在包頭的位置, 約定一個(gè)包總長(zhǎng)度的字段, 從而就知道了包的結(jié)束位置;
- 對(duì)于變長(zhǎng)的包, 還可以在包和包之間使用明確的分隔符(應(yīng)用層協(xié)議, 是程序猿自己來(lái)定的, 只要保證分隔符不和正文沖突即可)
8. TCP異常情況
- 進(jìn)程終止: 進(jìn)程終止會(huì)釋放文件描述符, 仍然可以發(fā)送FIN. 和正常關(guān)閉沒(méi)有什么區(qū)別.
- 機(jī)器重啟: 和進(jìn)程終止的情況相同.
- 機(jī)器掉電/網(wǎng)線斷開(kāi): 接收端認(rèn)為連接還在, 一旦接收端有寫入操作, 接收端發(fā)現(xiàn)連接已經(jīng)不在了, 就會(huì)進(jìn)行reset. 即使沒(méi)有寫入操作, TCP自己也內(nèi)置了一個(gè)?;疃〞r(shí)器, 會(huì)定期詢問(wèn)對(duì)方是否還在. 如果對(duì)方不在, 也會(huì)把連接釋放.
詳細(xì)的可以參考這篇文章:TCP 異常斷開(kāi)連接分析_tcp斷連問(wèn)題剖析-CSDN博客