1. 再談 UDP?
????????
????????報文長度:也是 2 個字節, 0 - 65535,也就是 64 kb。這表示一個 UDP 數據包一次最多只能傳輸 64 kb 的數據
? ? ? ? 校驗和:驗證數據是否在傳輸過程中發生修改。數據在傳輸過程中可能受到信號干擾,發生 “比特翻轉”。若 UDP 計算的兩次校驗和不一致,則直接丟棄該數據包。
2. 再談 TCP
? ? ? ? ?報頭長度:4 bit,表示 0-15,單位為 4 字節(32 bit),則最大表示 60 字節。TCP 報頭中除 “可選的” 外,均為固定長度,共 20 字節。則 “可選的” 最多 40 字節。
? ? ? ? 保留:預留出的空閑位置,以備后續拓展。
? ? ? ? 序號和確認序號:序號和確認序號組成 TCP 的序列號系統,用于保證 TCP 傳輸數據的順序正確。
2.1 TCP 傳輸管理之確認應答
? ? ? ? TCP 的確認應答機制保證數據在傳輸層的順序正確性,而無需應用層手動驗證順序。
????????發送方為每個字節數據分配的唯一序列號,用于標記數據在數據流中的位置。例如:發送一段 1000 字節的數據,首字節序列號為 100,則后續字節序列號為 101~1099。發送方按序發送數據,序號中填寫起始序列號。
????????確認序號(Ack)表示接收方 “期望接收的下一個字節的序列號”,隱含前序數據已正確接收。例如:接收方收到序列號 100~1099 的數據后,會返回 Ack = 1100,表示 “已正確接收前 1000 字節,期待下一個字節從 1100 開始”。發送方只有收到 ACK (TCP 報頭標志位中的 ACK 位為 1)后,才確認數據已成功接收,否則超時重傳。? ??
????????接收方收到數據后,會根據序列號將數據放入接收緩沖區。若數據有序(序列號連續),則按序交付上層應用;若數據失序(序列號不連續),則緩存該數據,等待缺失的中間數據到達后再重組交付。
????????例如,發送方依次發送 Seq = 100、200、300 的數據段,若接收方僅收到 Seq = 100 和 300,則接收方會緩存 Seq = 300 的數據(因失序暫時無法交付上層),僅返回 Ack = 200,表示 “期待從 200 開始的字節”,隱含 Seq = 100 之前的數據已確認,Seq = 200 及之后未確認。
2.2 TCP 傳輸管理之超時重傳
? ? ? ? 保證 TCP 可靠傳輸?的關鍵機制是 “確認應答” 和 “超時重傳”,而不是 “三次握手”。這些機制都體現了協議分層?“專業層做專業事” 的原則:傳輸層處理分包細節,應用層專注業務邏輯。(若使用 UDP 開發則需要應用層手動處理分包和重發。不過現在的 UDP 已經被 QUIC 封裝,HTTP/3 + QUIC 已經實現了更高級的可靠傳輸機制)
2.3 TCP 連接管理之三次握手
? ? ? ? 注:
? ? ? ? TCP 對于最后?ACK 丟失的處理。
????????客戶端發送 ACK 后即進入 ESTABLISHED 狀態。正常情況下,ESTABLISHED 狀態的客戶端不應該收到 SYN=1 的包,因為 SYN 標志僅用于連接建立階段。如果收到 SYN=1 的包,客戶端會判斷這是服務器對丟失 ACK 的重傳,則忽略 SYN 標志并重發?ACK。
? ? ? ? 關于三次握手的底層細節。
? ? ? ? 服務器創建監聽描述符 listen_fd,進入 LISTEN 狀態。
????????監聽描述符是服務器專用于接收客戶端連接請求的套接字描述符,服務器通過調用 listen() 將普通套接字轉換為監聽狀態(Java 中無需顯式調用 listen(),因為 ServerSocket 已經封裝了這一步操作)。
????????監聽描述符不參與數據傳輸,僅維護一個未完成連接隊列(SYN Queue)和一個已完成連接隊列(Accept Queue)。多個客戶端連接請求由同一個監聽描述符處理。
? ? ? ? 客戶端發送 SYN 數據包。IP 報頭包括源 IP(客戶端 IP)和目的 IP(服務器 IP)。TCP 報頭包括源端口(客戶端隨機高位端口)和目的端口(服務器主動指定的端口)。載荷主要包括隨機生成的初始序列號 Client_ISN、
自己能接收的數據緩沖區大小(窗口大小)、期望接收的單個數據段最大字節數(MSS)。
????????服務器內核解析出源?IP、源端口,與自身 IP 和端口組合成五元組,用于標識該連接(底層協議的基礎設施)。并通過監聽描述符接收載荷,將連接放入?SYN Queue。
? ? ? ? 服務器通過監聽描述符響應 SYN + ACK 數據包。主要包括確認序號(數值為 Client_ISN + 1)、隨機生成的初始序列 Server_ISN、窗口大小、MSS 等。
? ? ? ? 客戶端收到 SYN + ACK 后,發送 ACK 數據包,確認序號為??Server_ISN?+ 1。客戶端狀態變為 ESTABLISHED,服務器在收到 ACK 后,連接轉移至 Accept Queue,狀態也變為?ESTABLISHED,連接正式建立。
? ? ? ? 服務器調用?accept(),該方法會從 Accept Queue 中取連接,并為每個連接生成連接描述符(conn_fd)。后續數據傳輸通過 conn_fd?完成,監聽描述符繼續監聽新連接請求。
????????每個 TCP 連接對應一個套接字結構體(Struct Sock),包含發送緩沖區、接收緩沖區、連接狀態等信息。
2.4 TCP 連接管理之四次揮手
????????TCP 四次揮手在 Java 代碼中的核心觸發點是?socket.close()
?方法,該方法會觸發 FIN 包的發送。而 ACK 包的發送和接收由 Java 底層的 TCP 協議棧自動處理,無需手動干預。
? ? ? ? 由于主動斷開連接的一方既有可能是客戶端,也可能是服務器(雖然實際上大部分是客戶端),此處用主動和被動來區分通信雙方,而不用客戶端和服務器。
? ? ? ? 注:
? ? ? ??被動方的 ACK 和 FIN 不能合并表示。
? ? ? ? 因為它們的發送時機不一定相同。被動方收到主動方的 FIN 后,Java 底層的 TCP 協議棧會自動發送 ACK,而 FIN 要等到被動方在應用層代碼調用 close 方法才會發送。
2.5 TCP 效率機制之滑動窗口與快速重傳
? ? ? ? TCP 保障傳輸的可靠性需要犧牲效率,滑動窗口是 TCP 為了提升效率而產生的機制。不過,滑動窗口只是在一定程度上提高了效率,比起 UDP 這種協議,TCP 依然效率比較低。
? ? ? ? 在滑動窗口機制下,數據的發送方不再是接收一條 ACK 才發送下一條消息,而是連續發送一定量的消息后再等待(能夠連續發送的最大消息量取決于雙方協商的窗口大小)。
? ? ? ? 發送前幾條消息時,無需等待任何 ACK,直接發送。相當于將分開等待多個 ACK 的時間合并成一個。發送方收到 ACK 后,滑動窗口向后移動。網絡中可能存在后發先至的情況,假設發送方先收到接收方對第二條數據的 ACK,那么滑動窗口直接向后移動兩格即可。因為這條 ACK 的意思是,之前的數據都已確認,發送方就沒必要再維護前面的數據了,對第一條數據的 ACK 也沒意義了。
? ? ? ? 為了維護這個滑動窗口,內核會開辟發送緩沖區和接收緩沖區來記錄數據的應答和使用情況。所以這也是空間換時間的體現。
? ? ? ? 滑動窗口提升效率的地方就在于,它合并了一部分等待 ACK 和發送數據的時間。假設窗口大小為 4000 個字節,發送方收到 ack = 1000 時,就可以發送 seq = 4000 的數據了,發送完這條數據,它大概率也能收到 ack = 2000 了。
? ? ? ? 在滑動窗口過程中丟包,若丟的是 ACK,其實沒有關系,因為后面的 ACK 會覆蓋丟失 ACK 的含義。若丟的是發送方的數據,假設 seq = 3000 丟失,那么接收方一定會一直索要從 3000 開始的數據,其他序號的數據會被存入接收方的發送緩沖區中。無論接收方收到序號多么靠后的數據,它都不會索要后面的數據,因為前面尚有缺失的數據。當發送方連續收到三個相同的 ACK,就會立即重發這條數據,這種機制叫做快速重傳。
????????
2.6 TCP 安全機制之流量控制
? ? ? ? TCP 可以根據接收端的處理能力來決定發送端的發送速度,這個機制被稱為流量控制。接收方將自己接收緩沖區的剩余大小填入 TCP 首部中的 “窗口大小” 字段,通過 ACK 告知發送方。
? ? ? ? 發送方會根據這個數字,動態調整發送速度。數字越大,表示網絡吞吐率越高。
? ? ? ? 如果接收方緩沖區已滿,則會將窗口置為 0,此時發送方不再發送數據。但發送方仍需定期發送一個窗口探測包,這個報文只是為了觸發 ACK,使接收方將窗口大小告知發送方。
? ? ? ? TCP 首部中還包含窗口擴大因子字段,通過這個字段來擴展窗口大小,使其不再拘泥于 65535 字節。
2.7 TCP 安全機制之擁塞控制
? ? ? ? 流量控制根據接收方的處理能力進行限制,擁塞控制根據傳輸設備的轉發能力進行限制。在兩臺設備建立 TCP 連接之初,如果一方在不清楚當前網絡狀態的情況下,貿然發送大量數據,可能引發許多問題。
? ? ? ? 因此 TCP 采用 “慢啟動” 模式,先發送少量數據探測當前的網絡擁堵狀態,再決定傳輸速度。
? ? ? ? 發送方會維護一個變量,這個變量稱為擁塞窗口。發送開始時,擁塞窗口大小為 1,每收到一個應答,擁塞窗口加 1。取擁塞窗口和滑動窗口中較小的值作為實際發送的窗口。這里的擁塞窗口實際上描述的是最大報文段長度的倍數,因此初始時擁塞窗口會呈指數增長。
? ? ? ? 當增長到一個慢啟動的閾值時,改為線性增長。
????????
2.8 TCP 效率機制之延遲應答
? ? ? ? 接收方如果在收到數據后立刻回復 ACK,可能會返回一個較小的窗口值。這是因為接收方剛剛接收完數據,緩存區被占用。因此這個窗口值并不能真實反映接收方的處理能力。有可能接收方處理速度很快,但仍立即返回了一個較小的窗口,此時接收方遠沒有達到自己的極限。
? ? ? ? 如果接收方稍作等待,再進行應答,那么此時的窗口值就可以比較正確地反映其真實處理能力了。
? ? ? ? 延遲應答的模式分為,隔幾個包應答一次,隔一段時間應答一次。因為 ACK 的特點是,后一個可以覆蓋前一個的內容,并且滑動窗口的機制也使得 TCP 不會一直等待某個未歸的 ACK,因此少幾個 ACK 沒什么影響。
? ? ? ? 但也不能延遲太長時間,不能引起發送方重發數據。
2.9 TCP 效率機制之捎帶應答
? ? ? ? 捎帶應答是指在同一個 TCP 包中既發送數據又發送確認應答的一種機制。
? ? ? ? 捎帶應答是在延遲應答的基礎上實現的。ACK 報文實際上就是在 TCP 報頭中設置幾個字段,比如標志位中的 ACK 位設為 1,設置合適的窗口大小和確認序號等,并不攜帶具體數據。為了提高網絡利用率,可以將其與響應數據合并應答,但前提是必須有延遲應答這一機制提供基礎。
3. 經典問題
? ? ? ? 如何基于 UDP 實現更為可靠的傳輸?
? ? ? ??在應用層實現 TCP 的可靠性機制。比如,引入序列號,采用確認機制,超時重傳,數據分片與重組的控制,流量控制,擁塞控制等。目前已經有一些成熟方案,如 QUIC。具體實現需根據實際業務場景靈活調整機制的嚴格程度,最終目的是在效率和可靠性之間取得平衡。
????????UDP 大小受限,如果想基于 UDP 協議傳輸超過 64 kb 的數據,應該怎樣做?
? ? ? ? 在應用層實現數據分片和重組機制。不僅要考慮 UDP 的分片,也要考慮到當前鏈路層的 MTU。為了實現分片和重組,每個分片需維護額外信息,這包括:會話標識用于區分不同傳輸任務,總片數用于告知接收方重組時機,序號,確認序號,等等。
????????為什么要三次握手?
? ? ? ? 其一,確保通信路徑暢通,保證連接合法性。三次握手中,通信雙方均需確認彼此的響應能力,避免資源浪費。
? ? ? ? 其二,同步參數,確保兼容。告知對端自己隨機生成的初始序列號,而不是每次連接都使用同一個初始序列號,避免新舊連接的數據包混淆。協商適合雙方網絡路徑的數據包大小,避免分片。告知對方接收緩沖區容量,實現流量控制。
? ? ? ? 解釋?SYN 洪水攻擊。TCP 怎樣解決這個問題?
????????SYN Flood 是指,攻擊者向服務器發送大量虛假 SYN 包(使用隨機源 IP),服務器回復 SYN+ACK 后,永遠不能收到返回的 ACK(因為源 IP 是偽造的)。此時服務器積累大量半開連接,表現為服務器中大量進程處于 SYN_RCVD 這個中間狀態。
? ? ? ? 但服務器仍會為這些半開連接分配資源,比如會將連接維護在未完成連接隊列(SYN Queue)中。如果 SYN Queue 被惡意連接占滿,那么合法請求將無法進入。
? ? ? ? 通常使用 SYN Cookies 技術來抵御 SYN Flood 攻擊。在正常的 TCP 三次握手時,服務器收到 SYN 包會先分配半連接隊列資源,再返回 SYN+ACK 包。而 SYN Cookies 技術下,服務器收到 SYN 包后不創建半連接狀態,而是根據 SYN 包中的源地址、源端口、目的地址、目的端口等通過加密計算出一個 Cookie,并將其作為 SYN+ACK 包的初始序列號發送出去。
? ? ? ? 此時正常請求的客戶端會響應?ACK,該 ACK 的確認號為 Cookie + 1。服務器收到該 ACK 后,首先根據 IP 報頭,TCP 報頭等信息重新計算一次 Cookie,若與 ACK 的確認號 - 1 一致,則直接建立全連接。
? ? ? ? 該技術的優勢在于,其僅通過協議棧優化而在軟件上實現防御,無需硬件輔助。而缺點在于,服務器不再維護半開連接也就意味著犧牲了一部分 TCP 的參數協商功能,如 MSS 被設為默認值,可能降低傳輸效率。因此該技術通常在半連接隊列即將滿時自動啟用,以避免正常場景下的性能損耗。
? ? ? ? 解釋?TIME_WAIT。TIME_WAIT 為何持續?2MSL?
? ? ? ??MSL 全稱?Max Segment Life,即 TCP 報文最大生存時間。TIME_WAIT 持續存在 2MSL 可以保證兩個傳輸方向上都沒有尚未被接收或遲到的報文段(第一個 MSL 可以保證最后的 ACK 過期,第二個 MSL 可以保證可能重發的 FIN 過期)。這可以規避兩個問題。
? ? ? ? 其一,避免服務器由于立即重啟并復用相同端口而收到來自上一個進程的遲到的數據。
? ? ? ? 其二,讓主動關閉方有足夠時間等待被動關閉方可能重發的 FIN,并再次發送 ACK。
? ? ? ? 服務器大量出現 CLOSE_WAIT 是何原因?
? ? ? ? 處于 CLOSE_WAIT 的進程在等待上層協議調用 close 方法。如果該狀態持續時間很久,意味著應用層代碼無法執行到 close()。這代碼邏輯的 BUG,需要盡快排除。許多連接無法正常關閉,久而久之連接描述符被占滿,會導致資源泄露。
? ? ? ? 如何解決 TCP 粘包問題?
? ? ? ??這個問題在傳輸層無法解決,因為這是 TCP 面向字節流的特性導致的問題。UDP 不會出現這樣的問題,因為我們的發送和讀取都是以一個 UDP 數據包為單位的。
? ? ? ? 此時需要應用層協議明確包與包之間的界限。可以約定包的結束標記,也可以在每個包開頭預留字段表示包的長度。這兩種方式在 HTTP 協議中均有體現。
? ? ? ? TCP 如何應對異常情況?
? ? ? ? 1.?進程崩潰:進程崩潰和主動退出沒有本質區別,都會正常釋放文件描述符表,發送 FIN。TCP 的實現位于操作系統內核,而非用戶態進程,進程只是 TCP 連接的使用者。因此,即使進程終止,內核中的 TCP 協議棧仍能獨立完成四次揮手。
? ? ? ? 2. 正常關機:正常關機前,系統都會先 kill 掉所有的進程,所以和正常關閉沒什么區別。但理論上來說,四次揮手仍可能因時間不足而未完成。不過此時通信雙方都已進入揮手階段,如果某個結束報文遲遲沒有響應,那么超時后該設備會單方面釋放連接,并正常釋放連接資源。由于雙方只是未正常完成揮手的流程,因此對數據影響不是很大。
? ? ? ? 3. 接收方斷電或斷網:這屬于異常關機。發送方久久收不到任何 ACK,觸發重傳,重傳依舊無果,此時發送方會發送一個 RST 報文。RST 報文用于強制終止一個異常的 TCP 連接,跳過四次揮手,立即釋放連接資源。
? ? ? ? 4. 發送方斷電或斷網:接收方久久收不到任何數據,每間隔一定時間,接收方會發送 “心跳包” 用以嘗試觸發 ACK。多次發送無果,接收方發送 RST 報文強制關閉連接。TCP 協議的心跳包周期較長,真實場景下通常在業務層重新實現毫秒級的心跳包。