TCP的粘包問題以及資料的無邊界性

2021-07-12 07:04:43 字數 2488 閱讀 2925

上節我們講到了socket緩衝區和資料的傳遞過程,可以看到資料的接收和傳送是無關的,read()/recv() 函式不管資料傳送了多少次,都會盡可能多的接收資料。也就是說,read()/recv() 和 write()/send() 的執行次數可能不同。

例如,write()/send() 重複執行三次,每次都傳送字串"abc",那麼目標機器上的 read()/recv() 可能分三次接收,每次都接收"abc";也可能分兩次接收,第一次接收"abcab",第二次接收"cabc";也可能一次就接收到字串"abcabcabc"。

假設我們希望客戶端每次傳送一位學生的學號,讓伺服器端返回該學生的姓名、住址、成績等資訊,這時候可能就會出現問題,伺服器端不能區分學生的學號。例如第一次傳送 1,第二次傳送 3,伺服器可能當成 13 來處理,返回的資訊顯然是錯誤的。

這就是資料的「粘包」問題,客戶端傳送的多個資料報被當做乙個資料報接收。也稱資料的無邊界性,read()/recv() 函式不知道資料報的開始或結束標誌(實際上也沒有任何開始或結束標誌),只把它們當做連續的資料流來處理。

下面的**演示了粘包問題,客戶端連續三次向伺服器端傳送資料,伺服器端卻一次性接收到所有資料。

伺服器端** server.cpp:

#include

#include

#pragma

comment

(lib,

"ws2_32.lib"

)//載入 ws2_32.dll

#define buf_size 100

intmain();

//緩衝區

socket

clntsock =

accept

(servsock,

(sockaddr*)&clntaddr,

&nsize);

sleep

(10000

);//注意這裡,讓程式暫停10秒

//接收客戶端發來的資料,並原樣返回

int recvlen =

recv

(clntsock, buffer, buf_size,0);

send

(clntsock, buffer, recvlen,0);

//關閉套接字並終止dll的使用

closesocket

(clntsock);

closesocket

(servsock);

wsacleanup

();return0;

}

客戶端** client.cpp:

#include

#include

#include

#include

#pragma

comment

(lib,

"ws2_32.lib"

)//載入 ws2_32.dll

#define buf_size 100

intmain();

printf

("input a string: "

);gets

(bufsend);

for(

int i=

0; i<

3; i++)

//接收伺服器傳回的資料

char bufrecv[buf_size]=;

recv

(sock, bufrecv, buf_size,0);

//輸出接收到的資料

printf

("message form server: %s\n"

, bufrecv);

closesocket

(sock);

//關閉套接字

wsacleanup

();//終止使用 dll

system

("pause"

);return0;

}

先執行 server,再執行 client,並在10秒內輸入字串"abc",再等數秒,伺服器就會返回資料。執行結果如下:

input a string: abc

message form server: abcabcabc

本程式的關鍵是 server.cpp 第31行的**sleep(10000);,它讓程式暫停執行10秒。在這段時間內,client 連續三次傳送字串"abc",由於 server 被阻塞,資料只能堆積在緩衝區中,10秒後,server 開始執行,從緩衝區中一次性讀出所有積壓的資料,並返回給客戶端。

另外還需要說明的是 client.cpp 第34行**。client 執行到 recv() 函式,由於輸入緩衝區中沒有資料,所以會被阻塞,直到10秒後 server 傳回資料才開始執行。使用者看到的直觀效果就是,client 暫停一段時間才輸出 server 返回的結果。

client 的 send() 傳送了三個資料報,而 server 的 recv() 卻只接收到乙個資料報,這很好的說明了資料的粘包問題。

12 TCP的粘包問題以及資料的無邊界性

上節我們講到了socket緩衝區和資料的傳遞過程,可以看到資料的接收和傳送是無關的,read recv 函式不管資料傳送了多少次,都會盡可能多的接收資料。也就是說,read recv 和 write send 的執行次數可能不同。例如,write send 重複執行三次,每次都傳送字串 abc 那麼...

如何理解TCP協議是無邊界的,以及粘包?

時間版本修改 2020年4月2日 初稿乙個典型的協議頭設計如下 字段意義 包頭標識 uint8 2 協議版本號 uint8 當前版本號 clienttype uint8 客戶端型別 pc,安卓等 clientversion uint16 client版本 versiontype uint8 clie...

TCP 粘包問題以及解決

如果傳送端資料傳送過塊,接收端的資料接受過慢,接受端tcp內部的快取區域會溢位,無法再傳送資料,造成網路阻塞。所以每次要盡可能的把緩衝區資料讀出來,而不是每次讀一條訊息頭。因此要在應用層設定第二緩衝區,再從第二緩衝區讀資料,拆分包 完整說明 緩衝區 char szrecv recv buff szi...