文章目錄
前言
1. UDP
2. TCP
2.1 TCP 的三次握手
2.2 TCP 四次揮手
2.3 累計確認
2.4 順序問題和丟包問題
2.5 流量控制的問題
2.6 擁塞控制的問題
總結及面試問題
前言
前端的面試中常常問的 TCP 和 UDP 的區別,網上也有好多內容,好比html
TCP 和 UDP 的區別前端
TCP 是面向鏈接的,UDP 是面向無鏈接的
UDP程序結構較簡單
TCP 是面向字節流的,UDP 是基於數據報的
TCP 保證數據正確性,UDP 可能丟包
TCP 保證數據順序,UDP 不保證
以前也由於面試的緣由瞭解過一下,可是面試官又問了爲何 TCP 是可靠傳輸,一下就露餡了,說不出來了,而後這兩天就仔細瞭解了一下這方面的內容,還專門訂閱了極客時間的趣談網絡協議,所以,這篇文章主要基於趣談網絡協議和本身的理解。面試
1. UDP
要想理解 TCP 和 UDP 的區別,首先要明白什麼是 TCP,什麼是 UDP算法
TCP 和 UDP 是傳輸層的兩個協議緩存
咱們來看一下 UDP 的包頭服務器
由上圖能夠看出,UDP 除了端口號,基本啥都沒有了。若是沒有這兩個端口號,數據就不知道該發給哪一個應用。網絡
因此 UDP 就像一個小孩子,特別簡單,有以下三個特色數據結構
UDP 的特色tcp
溝通簡單,不須要大量的數據結構,處理邏輯和包頭字段
輕信他人。它不會創建鏈接,可是會監聽這個地方,誰均可以傳給它數據,它也能夠傳給任何人數據,甚至能夠同時傳給多我的數據。
愣頭青,作事不懂變通。不會根據網絡的狀況進行擁塞控制,不管是否丟包,它該怎麼發仍是怎麼發
由於 UDP 是"小孩子",因此處理的是一些沒那麼難的項目,而且就算失敗的也能接收。基於這些特色的話,UDP 可使用在以下場景中post
UDP 的主要應用場景
須要資源少,網絡狀況穩定的內網,或者對於丟包不敏感的應用,好比 DHCP 就是基於 UDP 協議的。
不須要一對一溝通,創建鏈接,而是能夠廣播的應用。由於它不面向鏈接,因此能夠作到一對多,承擔廣播或者多播的協議。
須要處理速度快,能夠容忍丟包,可是即便網絡擁塞,也絕不退縮,勇往直前的時候
基於 UDP 的幾個例子
直播。直播對實時性的要求比較高,寧肯丟包,也不要卡頓的,因此不少直播應用都基於 UDP 實現了本身的視頻傳輸協議
實時遊戲。遊戲的特色也是實時性比較高,在這種狀況下,採用自定義的可靠的 UDP 協議,自定義重傳策略,可以把產生的延遲降到最低,減小網絡問題對遊戲形成的影響
物聯網。一方面,物聯網領域中斷資源少,極可能知識個很小的嵌入式系統,而維護 TCP 協議的代價太大了;另外一方面,物聯網對實時性的要求也特別高。好比 Google 旗下的 Nest 簡歷 Thread Group,推出了物聯網通訊協議 Thread,就是基於 UDP 協議的
還有一些,可是寫的太多了也記不住,因此主要記住這幾個就夠了
2. TCP
首先是 TCP 的包頭格式
TCP 的包頭有哪些內容,分別有什麼用
首先,源端口和目標端口是不可少的。
接下來是包的序號。主要是爲了解決亂序問題。不編好號怎麼知道哪一個先來,哪一個後到
確認序號。發出去的包應該有確認,這樣能知道對方是否收到,若是沒收到就應該從新發送,這個解決的是不丟包的問題
狀態位。SYN 是發起一個連接,ACK 是回覆,RST 是從新鏈接,FIN 是結束鏈接。由於 TCP 是面向鏈接的,所以須要雙方維護鏈接的狀態,這些狀態位的包會引發雙方的狀態變動
窗口大小,TCP 要作流量控制,須要通訊雙方各聲明一個窗口,標識本身當前的處理能力。
經過對 TCP 頭的解析,咱們知道要掌握 TCP 協議,應該重點關注如下問題:
順序問題
丟包問題
鏈接維護
流量控制
擁塞控制
2.1 TCP 的三次握手
全部的問題,首先都要創建鏈接,因此首先是鏈接維護的問題
TCP 的創建鏈接稱爲三次握手,能夠簡單理解爲下面這種狀況
A:您好,我是 A
B:您好 A,我是 B
A:您好 B
至於爲何是三次握手我這裏就不細講了,能夠看其餘人的博客,總結的話就是通訊雙方全都有來有回
對於 A 來講它發出請求,並收到了 B 的響應,對於 B 來講它響應了 A 的請求,而且也接收到了響應。
TCP 的三次握手除了創建鏈接外,主要仍是爲了溝通 TCP 包的序號問題。
A 告訴 B,我發起的包的序號是從哪一個號開始的,B 一樣也告訴 A,B 發起的 包的序號是從哪一個號開始的。
雙方創建鏈接以後須要共同維護一個狀態機,在創建鏈接的過程當中,雙方的狀態變化時序圖以下所示
這是網上常常見到的一張圖,剛開始的時候,客戶端和服務器都處於 CLOSED 狀態,先是服務端主動監聽某個端口,處於 LISTEN 狀態。而後客戶端主動發起鏈接 SYN,以後處於 SYN-SENT 狀態。服務端接收了發起的鏈接,返回 SYN,而且 ACK ( 確認 ) 客戶端的 SYN,以後處於 SYN-SENT 狀態。客戶端接收到服務端發送的 SYN 和 ACK 以後,發送 ACK 的 ACK,以後就處於 ESTAVLISHED 狀態,由於它一發一收成功了。服務端收到 ACK 的 ACK 以後,也處於 ESTABLISHED 狀態,由於它也一發一收了。
2.2 TCP 四次揮手
說完創建鏈接,再說下斷開鏈接,也被稱爲四次揮手,能夠簡單理解以下
A:B 啊,我不想玩了
B:哦,你不想玩了啊,我知道了
這個時候,只是 A 不想玩了,即再也不發送數據,可是 B 可能還有未發送完的數據,因此須要等待 B 也主動關閉。
B:A 啊,好吧,我也不玩了,拜拜
A:好的,拜拜
這樣整個鏈接就關閉了,固然上面只是正常的狀態,也有些非正常的狀態(好比 A 說完不玩了,直接跑路,B 發起的結束得不到 A 的回答,不知道該怎麼辦或則 B 直接跑路 A 不知道該怎麼辦),TCP 協議專門設計了幾個狀態來處理這些非正常狀態
斷開的時候,當 A 說不玩了,就進入 FIN_WAIT_1 的狀態,B 收到 A 不玩了的消息後,進入 CLOSE_WAIT 的狀態。
A 收到 B 說知道了,就進入 FIN_WAIT_2 的狀態,若是 B 直接跑路,則 A 永遠處與這個狀態。TCP 協議裏面並無對這個狀態的處理,但 Linux 有,能夠調整 tcp_fin_timeout 這個參數,設置一個超時時間。
若是 B 沒有跑路,A 接收到 B 的不玩了請求以後,從 FIN_WAIT_2 狀態結束,按說 A 能夠跑路了,可是若是 B 沒有接收到 A 跑路的 ACK 呢,就再也接收不到了,因此這時候 A 須要等待一段時間,由於若是 B 沒接收到 A 的 ACK 的話會從新發送給 A,因此 A 的等待時間須要足夠長。
2.3 累計確認
TCP 如何實現可靠傳輸?
首先爲了保證順序性,每一個包都有一個 ID。在創建鏈接的時候會商定起始 ID 是什麼,而後按照 ID 一個個發送,爲了保證不丟包,須要對發送的包都要進行應答,固然,這個應答不是一個一個來的,而是會應答某個以前的 ID,表示都收到了,這種模式成爲累計應答或累計確認。
爲了記錄全部發送的包和接收的包,TCP 須要發送端和接收端分別來緩存這些記錄,發送端的緩存裏是按照包的 ID 一個個排列,根據處理的狀況分紅四個部分
發送而且確認的
發送還沒有確認的
沒有發送等待發送的
沒有發送而且暫時不會發送的
這裏的第三部分和第四部分就屬於流量控制的內容
在 TCP 裏,接收端會給發送端報一個窗口大小,叫 Advertised window。這個窗口應該等於上面的第二部分加上第三部分,超過這個窗口,接收端作不過來,就不能發送了
因而,發送端要保持下面的數據結構
對於接收端來說,它的緩存裏面的內容要簡單一些
接收而且確認過的
還沒接收,可是立刻就能接收的
還沒接收,但也沒法接收的
對應的數據結構以下
2.4 順序問題和丟包問題
結合上面的圖看,在發送端,一、二、3 已發送並確認;四、五、六、七、八、9 都是發送了還沒確認;十、十一、12 是還沒發出的;1三、1四、15 是接收方沒有空間,不許備發的。
在接收端來看,一、二、三、四、5 是已經完成 ACK 可是還沒讀取的;六、7 是等待接收的;八、9 是已經接收尚未 ACK 的。
發送端和接收端當前的狀態以下:
一、二、3 沒有問題,雙方達成了一致
四、5 接收方說 ACK 了,可是發送方還沒收到
六、七、八、9 確定都發了,可是 八、9 已經到了,六、7 沒到,出現了亂序,緩存着可是沒辦法 ACK。
根據這個例子能夠知道順序問題和丟包問題都有可能存在,因此咱們先來看確認與重傳機制。
假設 4 的確認收到了,5 的 ACK 丟了,六、7 的數據包丟了,該怎麼辦?
一種方法是超時重試,即對每個發送了可是沒有 ACK 的包設定一個定時器,超過了必定的事件就從新嘗試。這個時間必須大於往返時間,但也不宜過長,不然超時時間變長,訪問就變慢了。
若是過一段時間,五、六、7 都超時了就會從新發送。接收方發現 5 原來接收過,因而丟棄 5;6 收到了,發送 ACK,要求下一個是 7,7 不幸又丟了。當 7 再次超時的時候,TCP 的策略是超時間隔加倍。每當遇到一次超時重傳的時候,都會講下一次超時時間間隔設爲先前值的兩倍。
超時重傳的機制是超時週期可能相對較長,是否有更快的方式呢?
有一個快速重傳的機制,即當接收方接收到一個序號大於指望的報文段時,就檢測到了數據流之間的間隔,因而發送三個冗餘的 ACK,客戶端接收到以後,知道數據報丟失,因而重傳丟失的報文段。
例如,接收方發現 六、八、9 都接收了,可是 7 沒來,因此確定丟了,因而發送三個 6 的 ACK,要求下一個是 7。客戶端接收到 3 個,就會發現 7 的確又丟了,不等超時,立刻重發。
2.5 流量控制的問題
在流量控制的機制裏面,在對於包的確認中,會攜帶一個窗口的大小
簡單的說一下就是接收端在發送 ACK 的時候會帶上緩衝區的窗口大小,可是通常在窗口達到必定大小纔會更新窗口,由於每次都更新的話,剛空下來就又被填滿了
2.6 擁塞控制的問題
也是經過窗口的大小來控制的,可是檢測網絡滿不盡是個挺難的事情,因此 TCP 發送包常常被比喻成往誰管理灌水,因此擁塞控制就是在不堵塞,不丟包的狀況下儘量的發揮帶寬。
水管有粗細,網絡有帶寬,即每秒鐘能發送多少數據;水管有長度,端到端有時延。理想狀態下,水管裏面的水 = 水管粗細 * 水管長度。對於網絡上,通道的容量 = 帶寬 * 往返時延。
若是咱們設置發送窗口,使得發送但未確認的包爲通道的容量,就能撐滿整個管道。
如圖所示,假設往返時間爲 8 秒,去 4 秒,回 4 秒,每秒發送一個包,已通過去了 8 秒,則 8 個包都發出去了,其中前四個已經到達接收端,可是 ACK 還沒返回,不能算髮送成功,5-8 後四個包還在路上,還沒被接收,這個時候,管道正好撐滿,在發送端,已發送未確認的 8 個包,正好等於帶寬,也即每秒發送一個包,也即每秒發送一個包,乘以來回時間 8 秒。
若是在這個基礎上調大窗口,使得單位時間能夠發送更多的包,那麼會出現接收端處理不過來,多出來的包會被丟棄,這個時候,咱們能夠增長一個緩存,可是緩存裏面的包 4 秒內確定達不到接收端課,它的缺點會增長時延,若是時延達到必定程度就會超時重傳
TCP 擁塞控制主要來避免兩種現象,包丟失和超時重傳,一旦出現了這些現象說明發送的太快了,要慢一點。
具體的方法就是發送端慢啓動,好比倒水,剛開始倒的很慢,漸漸變快。而後設置一個閾值,當超過這個值的時候就要慢下來
慢下來仍是在增加,這時候就可能水滿則溢,出現擁塞,須要下降倒水的速度,等水慢慢滲下去。
擁塞的一種表現是丟包,須要超時重傳,這個時候,採用快速重傳算法,將當前速度變爲一半。因此速度仍是在比較高的值,也沒有一晚上回到解放前。
總結及面試問題
TCP 和 UDP 的區別
TCP 是面向鏈接的,UDP 是面向無鏈接的
UDP程序結構較簡單
TCP 是面向字節流的,UDP 是基於數據報的
TCP 保證數據正確性,UDP 可能丟包
TCP 保證數據順序,UDP 不保證
什麼是面向鏈接,什麼是面向無鏈接
在互通以前,面向鏈接的協議會先創建鏈接,如 TCP 有三次握手,而 UDP 不會
TCP 爲何是可靠鏈接
經過 TCP 鏈接傳輸的數據無差錯,不丟失,不重複,且按順序到達。TCP 報文頭裏面的序號能使 TCP 的數據按序到達報文頭裏面的確認序號能保證不丟包,累計確認及超時重傳機制TCP 擁有流量控制及擁塞控制的機制