**@tcp遇到黏包怎么解决?
这段时间复习下网络协议相关的知识, 在网上偶然看到tcp黏包的问题,就好奇地自己就实验了下,总结了下面几点经验,写的不对的还请指正:
一: 什么情况下tcp会发生黏包现象?
- 发送数据方多次send之间的间隔太短。tcp为了提高系统效率,使用了Nagle算法,假如多次短时间内使用send函数发送数据, Nagle会先将这几次send的数据存在内核的缓冲区,等缓冲区满了后或者超时(200ms)就会发送出去,这样就减少了发送的次数,提升了效率。
- 网路环境不好时,数据没有发送出去,就会全部堆积在缓冲区,等网络好后,就会一股脑的全部发送出去,这也会导致黏包。
- 接受数据方处理不当。tcp中接受方会将接受到的数据先放到缓冲区,然后应用程序再读取缓冲区的数据进行操作。假如缓冲区已经有个两个大小各为32个字节的数据,而此时直接读取64字节的数据的话,必然会造成黏包的。
二 : 怎么避免黏包问题?
- 关闭Nagle算法, 使用TCP_NODELAY选项:
int enable = 1;
setsockopt(sockfd,IPROTO_TCP,TCP_NODELAY(void*)&enable,sizeof(enable));
但我不建议这种做法,如果要进行文件传输,Nagle算法能大大提高效率。
- 尽量避免两次send时间过短,使用sleep或者usleep进行延时。但这也会影响程序运行效率,所以也不建议。
上面两种方法都是在数据发送时避免黏包,但假如网路不好或者接受处理不当还是会黏包,所以我推荐使用下面的方法:
- 格式化数据,每次发送的都是固定长度,每次接收的也是固定长度。
发送方:
char buf1[32] = "aaa";
char buf2[32] = "bbb";
send(sockfd, buf1, sizeof(buf1), 0); //每次固定发送32个字节
send(sockfd, buf2, sizeof(buf1), 0);
数组buf1里保存的数据长度只有3个字节,正常来说应该发送strlen(buf1) ,既3个字节,但因为此时连续发送了send了两次, 所以改为发送数组buf1长度,既32个字节。
接受方:
char buf[32] = {0};
ret = recv(fd, buf, sizeof(buf), 0); //每次接收固定长度32个字节
也可以将数据放再结构体里面。收发双发都约定好用相同大小的结构体或数组收发数据,这样就会避免黏包。
- 如果发送的数据不能固定长度,那每次发送数据时可以在数据前面加上这条数据的长度。例如规定数据的前四位为数据的长度length,接收方每次先接收四个字节,这四个字节中保存的就是应该接收的数据长度length,然后再次接收时就接收length个字节,这样也可以避免黏包。
发送方:
char buf[1024] = {0};
73 char send_msg[] = "aaa"; //需要发送的数据
74 int length = strlen(send_msg);
75
76 sprintf(buf, "%4d%s", length, send_msg);//length转化为字符串,并让length占4个字节
79 send(sockfd, buf, strlen(buf), 0);
接收方:
char buf[1024] = {0};
66 while(1)
67 {
68 //从TCP连接读取数据
70 ret = recv(fd, buf, 4, 0); //接收4个字节的长度信息
71 if(-1 == ret)
72 {
73 perror("recv1");
74 }
75 else if(0 == ret)
76 {
77 printf("客户端退出\n");
78 break;
79 }
81 printf("buf_length = %s\n", buf);
82 int length = atoi(buf); //字符串转化为整数
83 printf("length = %d\n", length);
84 memset(buf, 0, sizeof(buf));
85
86 ret = recv(fd, buf, length, 0); //接收长度为length字节的数据信息
87 if(-1 == ret)
88 {
perror("recv2");
89 }
91 else if(0 == ret)
92 {
93 printf("客户端退出\n");
94 break;
95 }
96 printf("buf_msg = %s\n", buf);
memset(buf, 0, sizeof(buf));
}
打印结果:
等待客户端的连接....
接受客户端的连接fd = 4
buf_length = 3
length = 3
buf_msg = aaa
客户端退出
参考资料:
1.:https://baike.baidu.com/item/Nagle%E7%AE%97%E6%B3%95/5645172?fr=aladdin
2.https://blog.csdn.net/weixin_41047704/article/details/85340311