Github地址: https://github.com/wujin1989/cdk
TCP因为没有边界,所以会有粘包的问题。
看下面代码:
server.c
#include "cdk.h"
#include
#define BUFSIZE 20
void routine(sock_t s) {
int ret;
char rbuf[BUFSIZE];
while (true) {
memset(rbuf, 0, BUFSIZE);
ret = recv(s, rbuf, BUFSIZE, MSG_WAITALL);
if (ret <= 0) {
abort();
}
}
}
int main(void) {
sock_t s;
s = cdk_tcp_listen("0.0.0.0", "9999");
cdk_tcp_netpoller(s, routine, false);
return 0;
}
client.c
#include "cdk.h"
#define BUFSIZE 2
int main(void) {
int ret;
sock_t c;
char sbuf[BUFSIZE];
c = cdk_tcp_dial("192.168.0.105", "9999");
for (int i = 0; i < 10; i++) {
cdk_sprintf(sbuf, BUFSIZE, "%d", i);
ret = send(c, sbuf, BUFSIZE, 0);
if (ret <= 0) {
abort();
}
}
while (true);
return 0;
}
在client端用wireshark抓包如下:
client.c代码里明明每次发送2个字节数据,发送10次,但从上面抓包来看,tcp合并了client发送的数据包,这个就是TCP发送数据时的粘包。
怎么避免发送时的TCP粘包呢?修改client.c代码,添加TCP_NODELAY选项如下:
#include "cdk.h"
#define BUFSIZE 2
int main(void) {
int ret;
sock_t c;
char sbuf[BUFSIZE];
c = cdk_tcp_dial("192.168.0.105", "9999");
for (int i = 0; i < 10; i++) {
cdk_sprintf(sbuf, BUFSIZE, "%d", i);
cdk_tcp_nodelay(c, true);
ret = send(c, sbuf, BUFSIZE, 0);
if (ret <= 0) {
abort();
}
}
while (true);
return 0;
}
在client再次抓包,如下:
看起来,NODELAY选项可以解决发送数据的粘包现象。
但是接收数据还是会有可能导致粘包,因为接收端根本不知道每次读多少数据。
接下来看下TCP拆包,TCP的拆包原因是发送数据大于MSS导致的,server还用之前代码,client看下面代码:
client.c
#include "cdk.h"
#include
#define BUFSIZE 537
int main(void) {
int ret;
sock_t c;
char sbuf[BUFSIZE];
memset(sbuf, 1, BUFSIZE);
c = cdk_tcp_dial("192.168.0.105", "9999");
cdk_tcp_nodelay(c, true);
ret = send(c, sbuf, BUFSIZE, 0);
if (ret <= 0) {
abort();
}
while (true);
return 0;
}
上面代码,发送一个537个字节的数据,但是因为cdk内部设置了MSS为536。
所以当发送一个大于MSS(536)的数据时,TCP会进行拆包,用wireshark抓包,如下图:
可以看到,发送数据被分成两个包发送,一个536字节,一个1字节。
这个就是TCP的拆包现象。
如何解决TCP的粘包和拆包呢?
- 粘包解决方案:
1. 发送和接收定长数据包。
2. 发送消息带有分隔符。
3. 发送消息带头信息,头信息里包含数据的长度。
(cdk采用这种方案)
- 拆包解决方案:
发送数据不要超过MSS。
具体实现,可以参考cdk。
怎么样?tcp粘包,拆包是不是很容易。
快来尝试下吧。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)