TCP 粘包拆包

粘包問題

在 TCP 這種字節流協議上作應用層分包是網絡編程的基本需求。分包指的是在發生一個消息(message)或一幀(frame)數據時,經過必定的處理,讓接收方能從字節流中識別並截取(還原)出一個個消息。所以,「粘包問題」是個僞命題html

短鏈接分包

對於短鏈接的 TCP 服務,分包不是一個問題,只要發送方主動關閉鏈接,就表示一個消息發送完畢,接收方 read() 返回0,從而知道消息的結尾編程

TCP 發送機制

爲了提升 TCP 的傳輸效率,TCP 有一套本身的發送機制緩存

  • TCP 維持一個變量,它等於最大報文段長度 MSS。只要緩存中存放的數據達到 MSS 字節時,就組裝成一個 TCP 報文段發送出去
  • 由發送方的應用進程指明要求發送報文段,即 TCP 支持的推送(push)操做
  • 發送方的一個計時器期限到了,這時把當前已有的緩存數據裝入報文段(但長度不能超過 MSS)發送出去

長鏈接分包

對於長鏈接的 TCP 服務,分包有四種方法網絡

  • 消息長度固定
  • 使用特殊的字符或字符串做爲消息的邊界,例如 HTTP 協議的 headers 以「rn」爲字段的分隔符
  • 在每條消息的頭部加一個長度字段,這恐怕是最多見的作法
  • 利用消息自己的格式來分包,例如 XML 格式的消息中 <root>...</root> 的配對,或者 JSON 格式中的 { ... } 的配對。解析這種消息格式一般會用到狀態機(state machine)

複雜的分包

假如消息格式很是簡單,「消息」自己是一個字符串,每條消息有一個4字節的頭部,以網絡序存放字符串的長度。消息直接沒有間隙,字符串也不要求以 '0' 結尾tcp

發送兩條消息「hello」和「smartboy」,打包後的字節流共有21字節code

0x00, 0x00, 0x00, 0x05, 'h', 'e', 'l', 'l', 'o',
0x00, 0x00, 0x00, 0x08, 's', 'm', 'a', 'r', 't', 'b', 'o', 'y'

假設數據最終都所有到達,數據解析邏輯至少能正確處理如下各類數據到達的次序htm

  • 一個字節一個字節到達
  • 數據分兩次到達,第一次收到2個字節,不足消息的長度字段
  • 數據分兩次到達,第一次收到4個字節,恰好夠長度字段,可是沒有 body
  • 數據分兩次到達,第一次收到8個字節,長度完整,但 body 不完整
  • 數據分兩次到達,第一次收到9個字節,長度完整,但 body 也完整
  • 數據分兩次到達,第一次收到10個字節,第一條消息的長度完整、body 也完整,第二條消息長度不完整
  • 請自行移動和增長分割點,一共有超過 100 萬種可能(221-1)
  • 數據一次就所有到達

《TCP粘包拆包》 原文連接:https://blog.maplemark.cn/2019/04/tcp粘包拆包.html?utm=sfblog

相關文章
相關標籤/搜索