上節我們講到了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...