目錄
1. 應用層
2. 傳輸層
2.1 UDP協定
2.1.1 UDP的特點
2.2 TCP協定
3. TCP協定的主要機制
3.1 确認應答(ACK)機制
3.2 逾時重傳機制
3.3 連接配接管理機制
3.4 TCP協定中的狀态
3.5 滑動視窗機制
3.6 流量控制
3.7 擁塞控制
3.8 延遲應答
3.9 捎帶應答
3.10 面向位元組流
3.11 粘包問題
3.12 TCP異常情況
1. 應用層
2. 傳輸層
(傳輸層和網絡層是作業系統核心實作好的.普通程式猿不能修改)
核心功能:完成"端對端"的資料傳輸
傳輸層協定有很多~~最常用的有兩個:UDP/TCP
2.1 UDP協定
UDP協定端格式:
16位UDP長度, 表示整個資料報(UDP首部+UDP資料)的最大長度
如果校驗和出錯, 就會直接丢棄
2.1.1 UDP的特點
a)無連接配接: 知道對端的IP和端口号就直接進行傳輸, 不需要建立連接配接
b)不可靠: 沒有确認機制, 沒有重傳機制; 如果因為網絡故障該段無法發到對方, UDP協定層也不 會給應用層傳回任何錯誤資訊
c)面向資料報: 不能夠靈活的控制讀寫資料的次數和數量
面向資料報
應用層交給UDP多長的封包, UDP原樣發送, 既不會拆分, 也不會合并;
用UDP傳輸100個位元組的資料: 如果發送端調用一次sendto, 發送100個位元組, 那麼接收端也必須調用對應的一次recvfrom, 接 收100個位元組; 而不能循環調用10次recvfrom, 每次接收10個位元組
UDP的緩沖區
UDP沒有真正意義上的發送緩沖區. 調用sendto會直接交給核心, 由核心将資料傳給網絡層協定進行後續的傳輸動作
UDP具有接收緩沖區. 但是這個接收緩沖區不能保證收到的UDP報的順序和發送UDP報的順序一緻; 如果緩沖區滿了, 再到達的UDP資料就會被丢棄
UDP的socket既能讀, 也能寫, 這個概念叫做 全雙工
UDP使用注意事項
我們注意到, UDP協定首部中有一個16位的最大長度. 也就是說一個UDP能傳輸的資料最大長度是64K(包含UDP首部). 然而64K在當今的網際網路環境下, 是一個非常小的數字. 如果我們需要傳輸的資料超過64K, 就需要在應用層手動的分包, 多次發送, 并在接收端手動拼裝
2.2 TCP協定
TCP全稱為 "傳輸控制協定(Transmission Control Protocol"). 人如其名, 要對資料的傳輸進行一個詳細的控制
TCP協定段格式:
4位TCP報頭長度:
表示該TCP頭部有多少個32位bit(有多少個4位元組); 是以TCP頭部最大長度是15 * 4 = 60
6位标志位:
URG: 緊急指針是否有效
ACK: 确認号是否有效
PSH: 提示接收端應用程式立刻從TCP緩沖區把資料讀走
RST: 對方要求重建立立連接配接; 我們把攜帶RST辨別的稱為複位封包段
SYN: 請求建立連接配接; 我們把攜帶SYN辨別的稱為同步封包段
FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN辨別的為結束封包段
16位校驗和:
發送端填充, CRC校驗. 接收端校驗不通過, 則認為資料有問題. 此處的檢驗和不光包含 TCP首部, 也包含TCP資料部分
16位緊急指針:
辨別哪部分資料是緊急資料
3. TCP協定的主要機制
3.1 确認應答(ACK)機制
TCP将每個位元組的資料都進行了編号. 即為序列号
每一個ACK都帶有對應的确認序列号, 意思是告訴發送者, 我已經收到了哪些資料; 下一次你從哪裡開始發
确認序号在TCP中是有特定含義的~
表示接下來想要的下一條資料編号是啥.而不是收到的資料編号是啥~
(這樣的設定是非常有用的)
發送方收到應答資料的時候.應答封包中的确認序号為1001,此時發送方就知道了, 1-1000的資料已經順利抵達.并且接下來要發送的資料就是從1001開始~
3.2 逾時重傳機制
若回應丢包,那麼發送方就會繼續發送資料,會造成資料重複
TCP自身已經處理好了這種資料重複的情況
1. TCP有一一個"接受緩沖區" (一塊記憶體)
TCP就會檢查緩沖區中有沒有重複的資料~根據序号就能去重
2. 逾時重傳等待的時間間隔,不是固定的,而是逐漸變大的
假設丢包的機率是固定值,此時連續丢包的機率其實是比較小的
一旦真的出現連續丢包,大機率是網絡出現了徹底的問題(網線斷開)是以, TCP重傳的時間間 隔,就會越來越高~頻率就會越來越低
重傳到一定的次數就會嘗試斷開連接配接~
Linux中(BSD Unix和Windows也是如此), 逾時以500ms為一個機關進行控制, 每次判定逾時 重發的逾時時間都是500ms的整數倍
如果重發一次之後, 仍然得不到應答, 等待 2*500ms 後再進行重傳
如果仍然得不到應答, 等待 4*500ms 進行重傳. 依次類推, 以指數形式遞增
累計到一定的重傳次數, TCP認為網絡或者對端主機出現異常, 強制關閉連接配接
3.3 連接配接管理機制
在正常情況下, TCP要經過三次握手建立連接配接, 四次揮手斷開連接配接
三次握手:
三次握手的本質,其實是确認通信雙方,發送能力和接受能力都正常
舉例了解:
真實情況 :
四次揮手:
斷開連接配接時,主機B給主機A回複的FIN和ACK能否合并在一起呢?
不一定~~可能能合并,也可能不會合并
不會合并的理由是發送ACK和FIN的時機是不一樣的.發送ACK是作業系統核心的行為.在收到FIN的第一時間,回複ACK,發送FIN是應用程式的行為.在代碼中執行到對應的"close()" 才會觸發FIN
會合并的理由是如果close能夠很快被調用到,(FIN和ACK的時間間隔不是很長的時候就會觸發TCP的"延時應答+捎帶應答"機制~~)
其實在三次握手和四次揮手之間也會涉及到"确認應答和逾時重傳"
3.4 TCP協定中的狀态
TCP協定是有“狀态”,重點了解TCP中的一些核心的狀态
1. ESTABLISHED連接配接成功,可以進行後續通信了.好比打電話,撥号撥通了一樣~
2. LISTEN伺服器端進入的狀态.伺服器準備就緒,允許用戶端随時來建立連接配接~~好比手機開機信号良好,随時可以有人來打電話~
3. CLOSE_ WAIT可以了解成一個斷開連接配接時的中間狀态.這個狀态正常情況下存在時間較短.出現在,收到FIN,傳回ACK,到發送FIN這個時間間隙中.一般看到這個狀态的時候,多半是代碼出bug了,導緻沒有及時調用到close方法~~
4. TIME_ WAIT也是斷開連接配接時的狀态~
3.5 滑動視窗機制
确認應答政策, 對每一個發送的資料段, 都要給一個ACK确認應答. 收到ACK後再發送下一 個資料段. 這樣做有一個比較大的缺點, 就是性能較差. 尤其是資料往返的時間較長的時候
既然這樣一發一收的方式性能較低, 那麼我們一次發送多條資料, 就可以大大的提高性能(其實是将多個段 的等待時間重疊在一起了)
a)視窗大小指的是無需等待确認應答而可以繼續發送資料的最大值. 上圖的視窗大小就是4000 個位元組(四個段)
b)發送前四個段的時候, 不需要等待任何ACK, 直接發送
c)收到第一個ACK後, 滑動視窗向後移動, 繼續發送第五個段的資料; 依次類推
d)作業系統核心為了維護這個滑動視窗, 需要開辟 發送緩沖區 來記錄目前還有哪些資料沒有應 答; 隻有确認應答過的資料 比特科技 , 才能從緩沖區删掉
e)視窗越大, 則網絡的吞吐率就越高;
視窗範圍内的資料就是已經發出去的資料,同時也是要等待ACK的資料.随着對方的ACK的到達,要等待的資料也就随之發送變化.同時也會發送新的資料出去
看起來就好像"視窗滑了一個格子一樣"
那麼如果出現了丢包, 如何進行重傳? 這裡分兩種情況讨論
情況一: 資料包已經抵達, ACK被丢了
這種情況下, 部分ACK丢了并不要緊, 因為可以通過後續的ACK進行确認
情況二: 資料包就直接丢了
當某一段封包段丢失之後, 發送端會一直收到 1001 這樣的ACK, 就像是在提醒發送端 "我想要 的是 1001" 一樣
如果發送端主機連續三次收到了同樣一個 "1001" 這樣的應答, 就會将對應的資料 1001 - 2000 重新發送
這個時候接收端收到了 1001 之後, 再次傳回的ACK就是7001了(因為2001 - 7000)接收端其實 之前就已經收到了, 被放到了接收端作業系統核心的接收緩沖區中
如果連續兩個包都丢了呢?
這裡的重傳機制,是比較精妙的~~得益于确認序号的設計
保證了傳輸的效率是比較高的
重傳的效率也是比較高的(沒有重傳多餘的資料)
這種機制被稱為 "高速重發控制"(也叫 "快重傳")
限制的方法就是流量控制
3.6 流量控制
接收端處理資料的速度是有限的. 如果發送端發的太快, 導緻接收端的緩沖區被打滿, 這個時候如果發送端 繼續發送, 就會造成丢包, 繼而引起丢包重傳等等一系列連鎖反應
是以TCP支援根據接收端的處理能力, 來決定發送端的發送速度. 這個機制就叫做流量控制(Flow Control)
a)接收端将自己可以接收的緩沖區大小放入 TCP 首部中的 "視窗大小" 字段, 通過ACK端通知發 送端
b)視窗大小字段越大, 說明網絡的吞吐量越高
c)接收端一旦發現自己的緩沖區快滿了, 就會将視窗大小設定成一個更小的值通知給發送端
d)發送端接受到這個視窗之後, 就會減慢自己的發送速度
e)如果接收端緩沖區滿了, 就會将視窗置為0; 這時發送方不再發送資料, 但是需要定期發送一個 視窗探測資料段, 使接收端把視窗大小告訴發送端
流量控制的目的是為了讓發送方的速率和接收方的速率盡可能一緻
步調一緻,才能做到可靠的同時盡量高效
在ACK封包中,告訴發送方,接收方的接受緩沖區的空閑空間
根據這個大小,發送方來決定接下來按照多大的視窗來傳輸速度~
3.7 擁塞控制
滑動視窗的大小由擁塞控制+流量控制. 一起決定的
雖然TCP有了滑動視窗這個大殺器, 能夠高效可靠的發送大量的資料. 但是如果在剛開始階段就發送大量 的資料, 仍然可能引發問題
由于網絡環境比較複雜,也不知道中間經曆了多少節點~~
是以發送方會采取一個動态變化的過程,來試探出目前的視窗大小多少合适
發送方會在初始情況下,設定一個比較小的 "視窗大小" (慢開始)發一下資料試試,如果沒丢包~,說明網絡暢通.就開始嘗試一個更大的視窗大小~ ,如果還沒丢包,網絡還是暢通,繼續嘗試一個更大的視窗大小~~直到出現丢包了,縮小視窗大小循環上述過程~~
滑動視窗的實際大小,就是擁塞視窗和流量控制視窗的較小值
擁塞視窗增長速度, 是指數級别的. "慢啟動" 隻是指初使時慢, 但是增長速度非常快
為了不增長的那麼快, 是以不能使擁塞視窗單純的加倍. 此處引入一個叫做慢啟動的門檻值
當擁塞視窗超過這個門檻值的時候, 不再按照指數方式增長, 而是按照線性方式增長
3.8 延遲應答
如果接收資料的主機立刻傳回ACK應答, 這時候傳回的視窗可能比較小
a)假設接收端緩沖區為1M. 一次收到了500K的資料; 如果立刻應答, 傳回的視窗就是500K
但實際上可能處理端處理的速度很快, 10ms之内就把500K資料從緩沖區消費掉了
b)在這種情況下, 接收端處理還遠沒有達到自己的極限, 即使視窗再放大一些, 也能處理過來
如果接收端稍微等一會再應答, 比如等待200ms再應答, 那麼這個時候傳回的視窗大小就是 1M
一定要記得, 視窗越大, 網絡吞吐量就越大, 傳輸效率就越高. 我們的目标是在保證網絡不擁塞的情況下盡量提高傳輸效率
那麼所有的包都可以延遲應答麼? 肯定也不是
數量限制: 每隔N個包就應答一次
時間限制: 超過最大延遲時間就應答一次
具體的數量和逾時時間, 依作業系統不同也有差異; 一般N取2, 逾時時間取200ms
3.9 捎帶應答
在延遲應答的基礎上, 我們發現, 很多情況下, 用戶端伺服器在應用層也是 "一發一收" 的. 意味着用戶端給 伺服器說了 "How are you", 伺服器也會給用戶端回一個 "Fine, thank you"
那麼這個時候ACK就可以搭順風車 , 和伺服器回應的 "Fine, thank you" 一起回給用戶端
在一次一問一答中,需要四次TCP資料的互動
一問+ACK
一答+ ACK
應用程式傳回響應的時候順帶把上個ACK資料也一起攜帶過去
減少了傳輸的資料包的個數,減低了通信成本,提高了效率~
3.10 面向位元組流
把TCP傳輸的資料(應用層資料包)想象成"水流一樣"
應用程式可以以位元組為機關來讀取/發送資料~
核心就是在于TCP的"緩沖區”
3.11 粘包問題
首先要明确, 粘包問題中的 "包" , 是指的應用層的資料包
在TCP的協定頭中, 沒有如同UDP一樣的 "封包長度" 這樣的字段, 但是有一個序号這樣的字段
站在傳輸層的角度, TCP是一個一個封包過來的. 按照序号排好序放在緩沖區中
站在應用層的角度, 看到的隻是一串連續的位元組資料
那麼應用程式看到了這麼一連串的位元組資料, 就不知道從哪個部分開始到哪個部分, 是一個完 整的應用層資料包
UDP不會粘包
那麼如何避免粘包問題呢?
歸根結底就是一句話, 明确兩個包之間的邊界
3.12 TCP異常情況
程序終止: 程序終止會釋放檔案描述符, 仍然可以發送FIN. 和正常關閉沒有什麼差別
機器重新開機: 和程序終止的情況相同
機器掉電/網線斷開: 接收端認為連接配接還在, 一旦接收端有寫入操作, 接收端發現連接配接已經不在了, 就會進行 reset. 即使沒有寫入操作, TCP自己也内置了一個保活定時器, 會定期詢問對方是否還在. 如果對方不在, 也會把連接配接釋放