一、TCP
TCP(Transmission Control Protocol),傳輸控制協議,對“傳輸、發送、通信”進行“控制”的協議,它充分地實現了數據傳輸時的各種控制功能,可以進行丟包時的重發控制,還可以對次序亂掉的分包進行順序控制。此外,TCP 是面向有連接的協議,只有在確認通信端存在時才會發送數據。
TCP 是一個傳輸層協議,提供 Host-To-Host 數據的可靠傳輸,支持全雙工,是一個連接導向的協議。
TCP 復雜控制連接的建立、斷開、保持等管理工作,保證了在 IP 這種無連接的網絡上也能夠實現高可靠性的通信。
TCP 使用場景:
- 遠程控制(SSH)
- File Transfer Protocol(FTP)
- 郵件(SMTP、IMAP)等
- 點對點文件傳出(微信等)
1. 數據發送
TCP 協議有這樣幾個基本操作:
- 一個 Host 主動向另一個 Host 發起連接,稱為 SYN(Synchronization),請求同步;
- 一個 Host 主動斷開請求,稱為 FIN(Finish),請求完成;
- 一個 Host 給另一個 Host 發送數據,稱為 PSH(Push),數據推送;
在 TCP 中,當發送端的數據到達接收主機時,接收端主機會返回一個已收到消息的通知,這個消息叫做確認應答(ACK)。如果在一定時間內沒有收到 ACK,發送端就可以認為數據已經丟失,並進行重發。
在 TCP 中,會在發送數據的每一個字節都標上序號,接收端查詢接收數據 TCP 首部中的序列號和數據的長度,將自己下一步應該接收的序號作為 ACK 返送回去。序列號機制使發送端可以根據序列號分批次發送,使接收端可以處理消息亂序和重復問題。
在 TCP 中,會在每次發包時計算往返時間及其偏差(方差),將這個往返時間和偏差(方差)相加就是 重發超時時間。當然,最初的數據包還不知道往返時間,其重發超時一般設置為 6 秒左右。若數據被重發之后還是收不到 ACK,則進行再次發送,此時,重發超時時間會以 2 倍、4 倍的指數函數延長。
當數據達到一定的重發次數之后,如果仍沒有任何 ACK 返回,就會判斷為網絡或對端主機發生了異常,強制關閉連接。
2. 連接管理
TCP 連接過程就是我們再熟悉不過的三次握手和四次揮手過程。TCP 是一個雙工協議,為了讓雙方都保證,建立連接的時候,連接雙方都需要向對方發送 SYN 和 ACK。

- 客戶端發送消息給服務端(SYN);
- 服務端准備好進行連接,針對客戶端的 SYN 給一個 ACK,並同時發送一個 SYN 給客戶端;
- 客戶端准備就緒,給服務端發送一個 ACK;
- 客戶端要求斷開連接,發送一個斷開的請求 —— FIN;
- 服務端收到請求,然后給客戶端一個 ACK,作為 FIN 的響應;
- 服務端把要處理的任務執行完,比如發送出去的消息還沒有得到響應、資源還沒得到釋放等。服務端經過一個等待,確定可以關閉連接了,再發一個 FIN 給客戶端;
- 客戶端收到請求,然后給服務端一個 ACK,作為 FIN 的響應,連接斷開;
FIN,其實就是我不會再發數據,但是你還可以發數據給我。
3. 段和窗口控制
TCP 以段(Segment)為單位發送數據,段的大小(MSS:Maximum Segment Size)是在三次握手的時候,在兩端主機之間被計算得出。兩端的主機在發出建立連接的請求時,會在 TCP 首部中寫入 MSS 選項,告訴對方自己的接口能夠適應的 MSS 的大小,然后 TCP 會在兩者之間選擇一個較小的值投入使用。下面是一份段結構:
TCP 段的大小(MSS)涉及發送、接收緩沖區的大小設置,雙方實際發送接收封包的大小,對拆包和粘包的過程有指導作用,因此需要雙方去協商。如果這個值設置的非常大,會降低性能,比如內存使用、資源占用等;如果這個值設置的非常小,會浪費傳輸資源(降低吞吐量);
TCP 以段為單位,每發一個段進行一次 ACK 的處理,這樣的傳輸方式有一個缺點 —— 包的往返時間越長通信性能就越低。為解決這個問題,TCP 引入了 窗口 這個概念,窗口是比段更大的單位,在窗口內發送了一個段以后不必要一直等待 ACK,而是繼續發送。如下圖,窗口大小為 4 個段。
有了窗口,發送方利用滑動窗口算法發送消息;接收方構造緩沖區接收消息,並給發送方 ACK。如下圖,這時候滑動窗口可以向右滑動。
在使用窗口控制中,如果出現段丟失該怎么辦?這個問題可以分為兩種情況,第一種情況是接收端接收到了數據,但是回復 ACK 失敗,這種情況是不需要再進行重發的,接收端會在下一次的 ACK 中告知數據接收成功了;第二種情況是接收端未收到數據,接收端會一直 ACK 該數據的序列號,當發送端連續 3 次接收同一序列號的 ACK,就會將其對應的數據進行重發,該機制稱為 高效重發機制。

4. 流控制
流控制體現為可以讓發送端根據接收端的實際接收能力控制發送的數據量。它的具體操作是,接收端主機向發送端主機通知自己可以接收數據的大小,於是發送端會發送不超過這個限度的數據,該大小限度就被稱為窗口大小。
實際操作中,每個 TCP 段的大小不同,限制數量會讓接收方的緩沖區不好操作,因此實際操作中窗口大小單位是字節數。
接收端的數據緩沖區一旦面臨溢出時,窗口大小的值也會被隨之設置為一個更小的值通知給發送端。發送端再根據該值,對發送數據的量進行控制。這就形成了一個完整的 TCP 流控制。
5. 擁塞控制
擁塞控制是為了解決網絡擁堵的問題,在網絡出現擁堵時,如果突然發送一個較大量的數據,極有可能會導致整個網絡的癱瘓。前面提到的流控制,窗口大小是由接收端決定的,發送端無法自我調節要發送的數據量。
為了在發送端調節所要發送數據的量,定義了一個叫做 擁塞窗口 的概念。在通信一開始時,通過一個叫做慢啟動的算法計算出擁塞窗口的初始閾值,之后每收到一次 ACK,擁塞窗口按照一定的比例放大擁塞窗口。在發送數據包時,將擁塞窗口的大小與接收端主動通知的窗口大小做比較,然后按照它們當中較小的那個值,發送比其還要小的數據量。
當 TCP 通信開始以后,網絡吞吐量會逐漸上升,但是隨着網絡擁堵的發生(體現為數據重發)吞吐量也會急速下降。於是會再次進入吞吐量慢慢上升的過程。因此所謂 TCP 的吞吐量的特點就好像是在逐步占領網絡帶寬的感覺。

6. Nagle 算法
Nagle 算法是指發送端即使還有應該發送的數據,但如果這部分數據很少的話,則進行延遲發送的一種處理機制。具體來說,就是僅在下列任意一種條件下才能發送數據。
- 已發送的數據都已經收到 ACK
- 已發送最大段長度(MSS)的數據
7. 延遲確認應答
前面提到,TCP 采用滑動窗口的控制機制,因此通常確認應答少一些也無妨。為此,引入了一個方法,那就是收到數據以后並不立即返回 ACK,而是延遲一段時間的機制。
- 在沒有收到 2x最大段長度(MSS)的數據為止不做 ACK,體現為每兩個數據段返回一個 ACK。
- 其他情況下,最大延遲 0.5s 發送 ACK(很多操作系統設置為 0.2s 左右)
二、UDP
UDP(User Datagram Protocol),用戶數據報協議,目標是在傳輸層提供直接發送報文(Datagram)的能力,不幫助拆分數據,Datagram 就是數據傳輸的最小單位,也不提供復雜的控制協議,利用 IP 提供面向無連接的通信服務。即使是出現網絡擁堵的情況下,UDP 也無法進行流量控制等避免網絡擁塞的行為。此外,傳輸途中即使出現丟包,UDP 也不負責重發。甚至當出現包的到達順序亂掉時也沒有糾正的功能。如果需要這些細節控制,那么不得不交由采用 UDP 的應用程序去處理。

UDP 不提供可靠性,不代表我們不能解決可靠性。UDP 的核心價值是靈活、輕量,構造了最小版本的傳輸層協議。在這個之上,還可以實現連接(Connection),實現會話(Session),實現可靠性(Relaability)等。所以理論上,任何一個用 TCP 協議構造的成熟應用層協議,都可以用 UDP 重構。
UDP 是一種沒有復雜控制,提供面向無連接通信服務的一種協議。換句話說,它將部分控制轉移給應用程序去處理,自己卻只提供作為傳輸層協議的最基本功能,本身處理既簡單又高效,因此經常用於以下幾個方面:
- 包總量較少的通信(DNS、PING、SNMP 等)
- 視頻、音頻等多媒體通信(即時通信)
- 限定於 LAN 等特定網絡中的應用通信
- 廣播通信(廣播、多播)
三、總結
TCP 和 UCP 比較可以從以下幾個方面着手:可靠性差異、連接vs無連接、流控技術(擁塞控制)、傳輸速度、應用場景差異。
TCP 是一種面向有連接的傳輸層協議。它可以保證兩端通信主機之間的通信可達。TCP 能夠正確處理在傳輸過程中丟包、傳輸順序亂掉等異常情況。此外,TCP 還能夠有效利用帶寬,緩解網絡擁堵。然而,為了建立與斷開連接,有時它需要至少 7 次的發包丟包,導致網絡流量的浪費。此外,為了提高網絡的利用率,TCP 協議中定義了各種各樣復雜的規范,因此不利於視頻會議(音頻、視頻的數據量既定)等場合使用。
UDP 有別於 TCP,它是一種面向無連接的傳輸層協議。UDP 不會關注對端是否真的收到了傳送過去的數據,如果需要檢查對端是否收到分組數據包,或者對端是否連接到網絡,則需要在應用程序中實現。UDP 常用於分組數據較少或多播、廣播通信以及視頻通信等多媒體領域。
TCP 相比 UDP 需要消耗更多的資源,比如必須先協商建立連接、每次 PSH 消息都需要 ACK 等。
近些年有一個趨勢,TCP/UDP 的邊界逐漸變得模糊,UDP 應用越來越多。比如傳輸文件,如果考慮希望文件無損到達,可以用 TCP。如果考慮希望傳輸足夠快,就可能會用 UDP。再比如 HTTP 協議,如果考慮請求/返回的可靠性,用 TCP 比較合適。但是像 HTTP 3.0 這類應用層協議,從功能性上思考,暫時沒有找到太多的優化點,但是想要把網絡優化到極致,就會用 UDP 作為底層技術,然后在 UDP 基礎上解決可靠性。