TCP 實際應用

淺談 TCP 介紹了 TCP 基礎的理論知識,這篇主要介紹 TCP 的一些應用場景,主要包括(SYN Flood,TCP 長連接和短連接,TCP/IP 實現)。

1. SYN Flood

SYN Flood 又稱 SYN 洪水,是利用 TCP 三次握手的漏洞產生的一種攻擊方式。
常用攻擊方式有兩種,最終結果都是造成服務端在等待 ACK。一種是客戶端只管發出 SYN,不理會服務端返回的 ACK。一種是客戶端構造錯誤的來源 IP,使服務端返回 ACK 到錯誤 IP。這兩種情況都會導致服務端無法正常收到客戶端發來的 ACK。使得 TCP 連接保持在半打開狀態,當 SYN 請求量過大時,會出現大量半打開狀態的 TCP 連接,當系統資源被消耗盡后,導致新的連接無法被創建,造成正常用戶無法訪問。

2. TCP 長連接/TCP 短連接

首先需要知道的是 TCP 連接建立需要三次握手,斷開連接需要四次,所以每次 TCP 的建立和關閉是需要消耗資源的。
TCP 短連接就是請求完成后關閉連接(一般是客戶端主動關閉)。
TCP 長連接是請求完成后仍保持 TCP 連接的狀態,可繼續傳輸數據。TCP 保活功能采用定時器來檢測連接狀態。客戶端可能正常運行,已經崩潰,重啟或不可達,服務端需要針對檢測的情況做出不同反應。

2.1 優缺點:

短連接管理簡單,存在的連接都是最近建立和使用的。但每次建立和關閉新連接會有資源消耗,當在大并發請求場景中使用,會出現大量的 TIME_WAIT 狀態的連接。導致資源的較大消耗。
長連接需要 TCP 保活定時器來檢測連接狀態。同時需要考慮不同長連接的負載均衡問題,但是可以在客戶端通過連接池得到部分解決,但在不同客戶端之間的負載均衡就無法做到。另外因為長連接存在只創建不關閉的情況,所以會導致建立的長連接越來越多。

2.2 短連接在請求量較大時,導致過多的 TIME_WAIT

參考 淺談 TCP 1.3 中的圖,因為 TCP 是雙工模式,所以每個方向的連接都需要單獨關閉。
主動關閉的一方發送 FIN 后,收到被動關閉一方的 FIN,隨后返回 ACK 信號,被動關閉一方完成關閉連接閉環,到達 CLOSED 狀態。
而主動關閉一方收到 FIN 后,發出 ACK,揮手已經結束,所以主動關閉一方不清楚被動關閉一方收到 ACK 沒有,所以只能等待超出 2MSL(Maximum Segment Lifetime 報文段最大生存時間)后,如沒有再次收到 FIN (重連)即可進行關閉。
2MSL 是因為被動關閉一方首先等待 MSL 如果沒收到 ACK 則重新發送 FIN,然后主動關閉一方等待 MSL 看是否能收到被動關閉一方重新發送的 FIN,如果沒有收到則表示被動關閉一方已成功收到了 ACK。

3. RPC 接口過度擔心性能

例如提供:get_topic_token_by_id 還是 get_topic_by_id?
當數據傳輸長度小于 MSS,數據仍被封裝在一個數據包中進行傳輸,所以不會導致性能的下降。

4. Socket 剖析

4.1 基礎 API

socket():創建一個 socket 描述符(socket descriptor)。
bind():將一個地址族中的特定地址與 socket 進行綁定。
listen():服務器通過 listen 來監聽請求。
connect():客戶端通過 connect 發出連接請求。
accept():內核生成一個全新的描述符,代表與客戶唯一的 TCP 連接。
read()/write():從文件描述符中讀取數據或寫入數據到文件描述符。
close():關閉 socket 描述符(TODO:accept() 和 socket() 創建的描述符都需要關閉嗎?)

4.2 blocking 和 non-blocking 區別?

在 Network IO 操作中會涉及到兩種系統對象,一個是調用這個 IO 用戶進程(user process),一個是系統內核(kernel)。

4.2.1 寫阻塞:

當調用 write 操作時,只是將要寫的數據復制到 kernel 的發送緩沖區,什么時候發送到網絡,什么時候對方接受,系統不進行通知和保證。所以當 kernel 的 send buffer 滿了之后就會造成 write 阻塞,即寫入的速度大于對方讀取的速度。

4.2.2 讀阻塞:

當調用 read 操作時,會首先檢查 kernel 的 receive buffer 中是否有數據,如果有數據則將數據拷貝到用戶進程中,如果沒有數據,blocking 和 non-blocking 進行會做出不同的反應。

4.2.3 blocking vs non-blocking

blocking IO 發現 kernel 中無數據,會進行等待直到有數據出現,然后再將數據從 kernel 拷貝到用戶進程。
non-blocking IO 發現 kernel 中無數據會直接返回 error。從用戶角度,進程不再需要等待,每次 read 操作都會得到一個結果。當進程發現 read 返回 error 后可以再次進行 read 操作。
關于 blocking IO/non-blocking IO 的詳細細節可以參考:IO - 同步,異步,阻塞,非阻塞 (亡羊補牢篇)

疑問:(TODO)
長連接導致報錯的原因?(TCP keepalive)
當 TCP 數據包被 socket 隔斷后如何重新組裝?(eg: return big_data)
當 Socket 讀寫發生錯誤時,如何進行數據重傳?

參考資料:

SYN flood - 維基百科
TCP的 TIME_WAIT 快速回收與重用
Socket 通信原理和實現
IO - 同步,異步,阻塞,非阻塞 (亡羊補牢篇)
TCP/IP 應用程序的通信連接模式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容