TCP協議
1.確認應答 實現可靠傳輸的核心機制
2.超時重傳 實現可靠傳輸的核心機制
3.連接管理 網絡部分最高頻的面試題
4.滑動窗口 提高傳輸效率的機制
5.流量控制 依據接收方的處理能力,限制發送方的發送速度。
6.擁塞控制 依據傳輸鏈路的處理能力,限制發送方的發送速度。
做實驗
1.先從比較小的窗口開始 慢啟動
2.指數增長
3.達到閾值,線性增長
4.遇到丟包,重新設定閾值,把窗口大小設為閾值的大小,繼續線性增長
TCP核心機制
核心機制七:延時應答
為了提高傳輸效率。
承接滑動窗口,讓傳輸效率提高一些。讓窗口盡量大一些(在可靠性的前提下)
不立即返回 ack,而是稍微等一等。接收方應用程序,就是消費者。 為了給接收方留出一些時間 好能夠多消費一些,接收緩沖區的剩余空間更大一點。
核心機制八 捎帶應答
網絡通信中,經常是“一問一答”的模型
客戶端發起request,服務器返回response (不是ack.....)
核心機制九:面向字節流
讀取/寫入的時候,讀寫操作有很多種方式,非常靈活。
讀100字節,
1. 一次讀10字節,10次完成
2. 一次讀20字節,5次完成
3. 一次讀50字節,2次完成 ……
因此引起了粘包問題
接收方用的時候 去掉報頭,把載荷內容放到一個 接收緩沖區中
接收方的應用程序,read的時候,就有很多種read的可能性
read的可能性:
1) a a a b b b c c c
2) aa ab bb cc c
3) aaa bbb ccc
4) aaab bbcc c
5) aa abb bcc c ......
取決于應用程序的代碼是咋寫的
具體怎樣來讀,才能確保讀到的是一個“完整的應用層數據包”?
粘包,粘的是應用層的數據包。
TCP字節流的特性,收到多個TCP數據報的時候把所有的載荷都給混到一起 放到接收緩沖區里?
包的邊界比較模糊,就好像“粘上了”一樣
解決粘包,從應用層入手,合理的設計應用層協議,讓包之間的邊界,能夠比較清晰
1. 通過特殊的分隔符,來作為包邊界的區分
比如,約定,每個應用層數據包,都以;為結尾
包的數據里面,不能包含分隔符
需要找合適的符號,確保這個符號在正文中不會重復出現
再比如,之前寫的回顯服務器當時是使用 \n 作為分隔符的(空白符)
讀到空白符就結束了。 空白符是統稱,包括不限于: 空格,換行,回車,制表符,分頁符,垂直制表符……
發請求的時候,使用 \n 作為結束標記的
由于是通過控制臺來進行輸入請求內容的
控制臺里輸入的內容本來就不會包含 \n
即使ascii碼表中,也有不少的字符,可以用來作為分隔符
有一些特殊的“不可見字符” 歷史遺留
2. 在應用層數據包開頭的地方,通過固定長度,約定整個應用層數據包的長度
應用程序read的時候,先固定read 2字節,看看2字節里的內容是啥 => 發現是3
接下來再read 3字節,讀到的aaa就是完整的應用層數據包
粘包問題只針對字節流的傳輸。對于文件操作,(使用文件存儲多個結構化數據...也是可能涉及到粘包的)
比如,通過文件,保存若干個學生信息。
class Student { name id age classId ... }
比如約定成,每個學生信息占一行(使用 \n 作為結束標記,作為分隔符)
對于UDP來說,不存在粘包問題,UDP的接收緩沖區和TCP的不太一樣
應用層不需要做區分
應用程序每次調用 recv 得到但就是一個完整的 DatagramPacket
也就對應一個完整的應用層數據包了。
未來實際開發中,很多時候是基于一些現成的框架/庫進行開發的
(很可能粘包問題已經被框架/庫幫我們解決了)
知名的框架和庫,都是“通用的”
核心機制十 異常情況
1. 進程崩潰 [正常的流程]
進程崩潰,意味著對應的文件描述符就被關閉了(調用close,干掉進程)。
只要是進程退出,都會釋放PCB,釋放文件描述符表
TCP的連接并沒有因為進程的結束立即結束,保留一會
觸發FIN
2. 主機關機 (正常流程)
正常流程下的主機關機,就會先殺死所有的進程
此時也會觸發FIN,進而進入四次揮手
有可能揮不完
如果關機的速度比較慢,有很大可能四次揮手揮完了
如果關機的速度比較快,剛發FIN,機器就關了
對端可以正常返回ack,也會繼續正常發送FIN,這里的FIN就沒有收到ack,嘗試重傳幾次FIN. 還是沒有ack,對端就直接放棄連接(刪除之前保存的對端信息)
四次揮手,如果揮完了, 雙方可以確認,雙方都能順利把保存的信息刪掉, 如果揮不完,至少自己可以把保存的信息刪掉, 對端就不管了 此時對端關機了,內存的數據全沒了
3. 主機掉電(直接拔電源)
直接啥都沒了
來不及發起FIN 臺式機
1) 如果掉電的一方是接收方,對方是發送方。
對方繼續發送數據,沒有ack=> 超時重傳 => 仍然沒有ack => 繼續超時重傳
達到一定程度,掉電方仍然沒有ack,發送方發送一個復位報文,就是表示要重置連接
放棄當前的連接
2) 如果掉電的一方是發送方,對方是接收方。
接收方感受到的是,發送方,突然停下了
接收方會繼續阻塞等待,等待發送方發來新的數據
心跳包。
接收方會周期性的和發送方交換“心跳包”
A給B發一個無業務數據的報文
B給A返回一個ack 如果對方有應答,就可以認為對方是正常工作的。
如果心跳包也沒有應答,就可以認為,對方掛了
如果發現心跳包沒有,就可以單方面的釋放連接了
這個機制非常重要,尤其是分布式系統中
分布式系統中,知道某個節點存活,非常重要的
4. 網線斷開
本質上和主機掉電是一樣的.
網線斷開的兩端,一個是發送方,一個是接收方 對于發送方來說,沒有ack => 超時重傳 => 仍然沒有ack => 超時重傳 => 達到一定程度,放棄連接
對于接收方來說,周期性觸發心跳包 => 發現對端下線 => 放棄連接
1. 確認應答 2. 超時重傳 3. 連接管理(三次握手,四次揮手) 4. 滑動窗口(快速重傳) 5. 流量控制 6. 擁塞控制 7. 延時應答 8. 捎帶應答 9. 面向字節流(粘包問題) 10. 異常情況(心跳包)
TCP有連接,可靠傳輸,面向字節流
還有一系列的機制……十個機制?
大部分情況優先考慮TCP
UDP無連接,不可靠傳輸,面向數據報 UDP傳輸效率高
機房內部的傳輸,不太會丟包,效率要求更高 還有的場景,既需要可靠性(不需要那么嚴格的可靠性),又需要效率(比TCP更高)
用UDP實現可靠傳輸(經典面試)
其實是在考察 TCP 基于UDP,在應用層,自己寫代碼,實現可靠傳輸.... 參考TCP的做法。
1) 確認應答 序號/確認序號 2) 超時重傳 3) 滑動窗口 4) 流量控制,擁塞控制 ……?