《Unity3D網絡遊戲實踐》(第2版)要點摘錄 – 「Tcp深入了解」

書名:《Unity3D網絡遊戲實踐》(第2版)

作者:羅培羽

所讀版本:機械工業出版社

4層網絡模型

  • 應用層

    • 應用程序層面

    • 把數據轉換成二進制流傳給傳輸層(Socket.Send)

  • 傳輸層

    • 對數據進行加工,並提供各種功能

      • 雙端約定收到信息後會給對方回應,確保信息的傳遞成功

    • 代表了TCP/IP中的「TCP」

      • 「IP」為網絡層

      • 「TCP」是在網絡層協議的基礎上,增加了數據拆分、確認重傳、流量控制的機制

        • 數據拆分

          • 為每個數據添加了20個字節的頭部信息,包含了「數據的編號」

  • 網絡層

    • 「IP」給傳輸層(TCP)數據添加上具體的目的地/本地的地址信息

  • 網絡接口

    • 數據通過物理介質進行傳輸和解析

數據傳輸流程(TCP)

連接建立
  • TCP是面向連接的,在發送數據前,雙方之間必須建立連接

    • A發送一封特殊信件(SYN)至B => B收到後回應一封(SYN/ACK)信件至A,此時A知道連接可達

  • 三次握手

    • 初始化連接,同步連接雙方的序列號、確認號、交換TCP窗口的大小信息

    1. 連接方調用Connect,向監聽方發送數據包(SYN)

      • SYN包含了序列號seq

    2. 監聽方收到SYN數據包後,由標志位SYN知道對方正在請求建立連接;然後將SYN/ACK數據包發送回去以確認連接請求

    3. 連接方收到SYN/ACK數據包後,Connect函數返回,連接成功;然後將ACK數據包發至監聽方

      • 假如Connect一直沒返回,代表底層正在等待和嘗試重發SYN或SYN/ACK數據包,直到連接成功/超出重試次數

      • 當監聽方收到ACK包,將連接狀態設為established時,代表連接成功建立

數據傳輸
  • 發送數據時,TCP會考慮對方緩沖區的容量,當對方緩沖區滿時,會暫停發送數據,防止對端溢出

  • 發送一個數據後,發送方不能確保數據被對方接收,發送方會等待接收方的回應

    • 如果太長時間沒有收到回應,發送方會重新發送數據

  • TCP會根據數據返回的時間判斷網絡是否擁堵,是則減慢發送的速度

連接終止
  • 四次揮手

    • 確保雙端釋放socket資源

    1. 主機1向主機2發送終止信號(FIN),主機1進入FIN_WAIT_1狀態,沒有需要發送的數據,等待主機2回應

    2. 主機2收到主機1發送的終止信號(FIN),向主機1回應1個ACK;收到ACK的主機1進入FIN_WAIT_2狀態

    3. 主機2把所有數據發送完畢後,向主機1發送終止信號(FIN),請求關閉連接

    4. 主機1收到主機2發送的終止信號(FIN),向主機2回應1個ACK,然後主機1進入TIME_WAIT狀態;主機2收到ACK後關閉連接

      • TIME_WAIT:等待一段時間,處理主機2的重發數據

常用TCP參數

  • ReceiveBufferSize

    • 指定OS接收緩沖區的大小,默認為8192字節

  • SendBufferSize

    • 指定OS發送緩沖區的大小,默認為8192字節

  • NoDelay

    • 是否使用Nagle算法(true為不使用)

      • 對程序頻繁發送數據量很小的數據進行優化

      • 讓這些數據累積到一定數量再組成一個大數據包發送出去

    • 優點:減少數據量

    • 缺點:降低了實時性

  • TTL

    • 發送的IP數據包的生存時間值(Time To Live)

    • IP頭中的一個值,表示一個IP數據報能夠經過的最大路由器跳數(默認值與OS相關,xp = 128, win7 = 64, win10 = 65, linux = 255)

    • 目的是避免IP在網絡中的無限循環收發

  • ReuseAddress

    • 端口複用,讓同一個端口可被多個socket使用

    • 目的:由於退出程序與釋放端口兩者是不同步的,因此,重啟服務器時,可能會因為端口還沒被釋放而導致端口綁定失敗;直到端口被釋放後才能成功重啟

  • LingerState

    • 設置socket保持連接的時間

      • 客戶端發起斷開連接的請求後,服務端會向客戶端發送一個FIN信號

      • 該信號讓客戶端進入TIME_WAIT狀態

        • 該狀態目的是讓服務端在斷開連接前處理未完成的事情

      • 通過LingerState來指定該等待時間

        • 時間==0,一直等待,直到發送完

        • 時間>0,等待指定時間後斷開連接

心跳機制

  • 斷開連接時,主動方會給對端發送FIN信號,開啟4次揮手流程;若主動方無法給對端發送FIN信號,對端會一直認為連接有效

  • TCP自帶一個檢測機制,如果在指定時間內沒有數據傳送,會給對端發送一個信號;對端如果收到,回送一個TCP信號;如果一段時間沒有收到響應,重試幾次仍失敗則視為網絡不通,關閉socket

    • Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true)
  • 心跳機制思路

    • 客戶端定時向服務端發送Ping消息

    • 服務端收到後回應Pong消息,並記錄客戶端最後一次發送Ping消息的時間

    • 如果服務端很久沒有收到Ping,就假定連接不通,關閉連接並釋放socket資源