偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

為什么 TCP 粘包是正?,F(xiàn)象

網(wǎng)絡
TCP 粘包 并不是因為協(xié)議本身有 “問題”,而是一種 “正?,F(xiàn)象”, 因為 TCP 是面向字節(jié)流的協(xié)議,數(shù)據(jù)之間沒有所謂 “邊界”,所以數(shù)據(jù)粘包之后的 “拆包” 工作應該 (也必須) 由應用層完成。

一、TCP 粘包現(xiàn)象

TCP 粘包是指發(fā)送方發(fā)送了若干數(shù)據(jù)包,但是到達接收方之后,數(shù)據(jù)包的內(nèi)容全部 “粘連” 在了一起,每個數(shù)據(jù)包的數(shù)據(jù)結(jié)尾直接和下個數(shù)據(jù)包的數(shù)據(jù)連在了一起,無法正常區(qū)分。

下面的圖片展示了一個 TCP 粘包現(xiàn)象,正常的 3 個數(shù)據(jù)包因為粘包,被連在了一起,看起來像是一個數(shù)據(jù)包,結(jié)果就很感人了 ...

二、TCP 粘包/拆包 原因


TCP 粘包 并不是因為協(xié)議本身有 “問題”,而是一種 “正?,F(xiàn)象”, 因為 TCP 是面向字節(jié)流的協(xié)議,數(shù)據(jù)之間沒有所謂 “邊界”,所以數(shù)據(jù)粘包之后的 “拆包” 工作應該 (也必須) 由應用層完成。


TCP 采用 “異步” 方式發(fā)送應用數(shù)據(jù),也就是說,當應用程序中調(diào)用 Send(packet) 發(fā)送數(shù)據(jù)時,雖然 Send 函數(shù)會立即返回,但是數(shù)據(jù)并不一定已經(jīng)到達通信對方了。應用數(shù)據(jù)具體什么時候發(fā),由應用層下面的傳輸層 TCP 說了算,TCP 使用了 3 個主要機制 (確認與重傳、滑動窗口流量控制、擁塞控制) 來實現(xiàn)可靠性傳輸,保證應用數(shù)據(jù)的可靠傳輸和 應用層數(shù)據(jù)發(fā)送順序和到達順序一致的語義保證。

下面來展開說一下可能導致 粘包/拆包 問題的原因。

1. 面向字節(jié)流的工作特性

TCP 作為傳輸層,并不了解 (也不關(guān)心) 應用層數(shù)據(jù)的上下文含義,它只會根據(jù)通信雙方約定的 MSS[1] 對發(fā)送緩沖區(qū)的數(shù)據(jù)包進行拆分。

所以在應用層的視角來看,一個完整的數(shù)據(jù)包 (可能是一段聊天文字、一個圖片、一個視頻) 可能會經(jīng)歷不同的發(fā)送過程:

  • 如果 MSS 較小,一個完整的數(shù)據(jù)包會被 TCP 拆分成多個更小的 數(shù)據(jù)包進行發(fā)送,產(chǎn)生拆包現(xiàn)象
  • 如果 MSS 較大,多個完整的數(shù)據(jù)包會被 TCP 合并為一個更大的 數(shù)據(jù)包進行發(fā)送,產(chǎn)生粘包現(xiàn)象

2. 緩沖機制

TCP 在發(fā)送數(shù)據(jù)時,會將數(shù)據(jù)放入發(fā)送緩沖區(qū);在接收數(shù)據(jù)時,會將數(shù)據(jù)放入接收緩沖區(qū)。TCP 會盡可能地將發(fā)送緩沖區(qū)中的數(shù)據(jù)打包成一個或多個數(shù)據(jù)包發(fā)送出去,而接收方在讀取數(shù)據(jù)時,也會盡量將接收緩沖區(qū)中的數(shù)據(jù)全部讀取出來。這種機制可能導致發(fā)送方一次發(fā)送的多個數(shù)據(jù)包被接收方一次性讀取,從而引發(fā)粘包問題。

  • TCP 發(fā)送方會將數(shù)據(jù)放入 發(fā)送緩沖區(qū)
  • TCP 接收方會將數(shù)據(jù)放入 接收緩沖區(qū)

為了盡可能提升發(fā)送數(shù)據(jù)和接受處理數(shù)據(jù)的性能,作為發(fā)送方來講:

  • 如果要發(fā)送的數(shù)據(jù)小于發(fā)送緩沖區(qū)大小,TCP 會將多次要發(fā)送的數(shù)據(jù),全部寫入發(fā)送緩沖區(qū),然后一起發(fā)送,產(chǎn)生粘包現(xiàn)象
  • 如果要發(fā)送的數(shù)據(jù)大于發(fā)送緩沖區(qū)大小,TCP 會將要發(fā)送的數(shù)據(jù)進行切分,滿足寫入發(fā)送緩沖區(qū)的條件,然后發(fā)送,產(chǎn)生拆包現(xiàn)象

作為接收方來講:

  • 如果應用層沒有及時處理接收緩沖區(qū)的數(shù)據(jù),產(chǎn)生粘包現(xiàn)象

3. Nagle 算法

Nagle 算法原理: 發(fā)送方已經(jīng)發(fā)送數(shù)據(jù)還未被接收方確認之前,期間如果又有小數(shù)據(jù)生成,先把小數(shù)據(jù)收集起來,湊滿一個 MSS (最大報文段大小) 或者收到接收方 Ack 后再一起發(fā)送。通過將小數(shù)據(jù)包積累成較大的數(shù)據(jù)包后再發(fā)送,從而提高網(wǎng)絡效率。

很顯然,根據(jù) Nagle 算法的工作機制,在頻繁發(fā)送小的數(shù)據(jù)包時 (例如 Telnet, SSH 終端),會產(chǎn)生粘包現(xiàn)象。

下面是在 Go 語言中關(guān)閉 TCP Nagle 算法的示例代碼。

package main

func main() {
    conn, _ := net.Dial("tcp", "dbwu.tech:443")

    fd := conn.(*net.TCPConn).File()
    // 關(guān)閉 Nagle 算法
    syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1)
}

? 單純關(guān)閉 Nagle 算法并不能解決粘包問題,讀者思考一下為什么?

?? 綜上所述,因為應用層和傳輸層工作方式上的差異,所以就導致了所以的 “TCP 粘包/拆包” 問題。

三、解決方案

既然拆包必須由應用層來完成,那么按照數(shù)據(jù)處理的思路,只要應用程序?qū)?TCP 的字節(jié)流數(shù)據(jù)能夠區(qū)分單個消息的標志和邊界,那么應用程序就可以將粘包后的數(shù)據(jù)分割為正常的單個消息,粘包問題自然迎刃而解。

目前業(yè)界主要采用的解決方案:

(1) 設(shè)置消息固定長度: 發(fā)送方可以將每個消息設(shè)置為固定的長度 (對于長度不夠的消息可以使用 0 進行填充),接收方從緩沖區(qū)讀取數(shù)據(jù)時,每次都讀取固定的長度,這樣很自然就把單個消息拆分出來

(2) 設(shè)置消息分隔符: 發(fā)送方在將單個消息末尾追加分隔符,用來分區(qū)單個消息,接收方從緩沖區(qū)讀取數(shù)據(jù)后,根據(jù)分隔符將讀取到的數(shù)據(jù)切割成一個個單條消息

當然,分隔符很容易出現(xiàn)在要發(fā)送的應用數(shù)據(jù)中,這樣就產(chǎn)生了沖突,無法進行正常拆包,所以實際項目中很少使用這種方式。

(3) 設(shè)置消息頭部格式: 將消息分為消息頭部和消息體,消息頭中包含表示消息總長度(或者消息體長度,例如 HTTP 中的 Content-Length)字段,接收方首先讀取消息頭部,然后根據(jù)消息長度字段 讀取具體的消息體內(nèi)容,這也是最常用的方式

(4) 特定消息格式: 例如將單個消息固定為 JSON 格式,接收方從緩沖區(qū)讀取數(shù)據(jù)后,根據(jù)讀取到的數(shù)據(jù)能否被解析成合法的 JSON 來判斷消息是否結(jié)束,當然,實際項目中很少使用這種方式

??注意,以上提到的解決方案通常由網(wǎng)絡編程框架 (例如 Java 的 Netty, Golang 的 gnet) 來實現(xiàn),而不是由應用程序中的業(yè)務代碼來實現(xiàn)。

四、不同應用場景的下的應對方案

完全禁止 粘包/拆包 的場景:

  • 低延遲要求: 在線游戲、股票交易
  • 小數(shù)據(jù)頻繁傳輸,例如 Telnet, SSH 終端

可以忽視 粘包/拆包 的場景:

  • 小數(shù)據(jù)傳輸,且兩次傳輸之間的時間間隔很大,例如 5 秒/次 的心跳
  • 使用已經(jīng)處理了粘包問題的應用層協(xié)議,例如 HTTP 使用響應頭中的 Content-Length 作為消息分隔符
  • 傳輸數(shù)據(jù)只需要保證順序即可,無需區(qū)分邊界,例如大文件傳輸,每個數(shù)據(jù)包都是文件的一小部分而已,因為 TCP 保證了傳輸順序性,所以接收方只需要讀取數(shù)據(jù),然后追加到已有數(shù)據(jù)的后面即可

五、UDP 有粘包/拆包 問題嗎?

UDP 是無連接的協(xié)議,每個數(shù)據(jù)報都是獨立傳輸?shù)?,接收方收到的?shù)據(jù)報和發(fā)送方發(fā)送的數(shù)據(jù)報是一一對應的 (但是報文到達時間上可能會出現(xiàn)亂序),不需要建立連接,也不需要維護連接的狀態(tài),換句話說,選擇 UDP 協(xié)議時,應用層必須完成數(shù)據(jù)的拆包工作,所以自然也就不存在 粘包/拆包 問題了。

責任編輯:趙寧寧 來源: 洋芋編程
相關(guān)推薦

2020-03-10 08:27:24

TCP粘包網(wǎng)絡協(xié)議

2021-07-15 10:35:16

NettyTCPJava

2024-12-19 11:00:00

TCP網(wǎng)絡通信粘包

2019-10-17 11:06:32

TCP粘包通信協(xié)議

2022-08-01 07:07:15

粘包半包封裝

2021-03-09 22:30:47

TCP拆包協(xié)議

2019-10-24 07:35:13

TCP粘包Netty

2020-12-30 09:04:32

Go語言TCPUDP

2022-04-28 08:38:09

TCP協(xié)議解碼器

2020-12-23 07:53:01

TCP通信Netty

2024-08-16 21:47:18

2020-01-13 10:16:53

TCPUDP協(xié)議

2020-01-15 08:42:16

TCP三次握手弱網(wǎng)絡

2020-04-01 15:30:19

TCPUDP服務器

2021-07-06 17:13:08

NVMe存儲協(xié)議數(shù)據(jù)中心

2020-01-06 15:23:41

NettyTCP粘包

2022-07-27 07:36:01

TCP可靠性

2019-09-30 09:41:04

五層協(xié)議OSITCP

2019-10-25 00:32:12

TCP粘包Netty

2025-03-21 10:31:44

點贊
收藏

51CTO技術(shù)棧公眾號