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

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