TCP粘包问题 转自CSDN
Linux网络编程之Socket(五):Tcp流协议产生的粘包问题和解决方案 - Meditation - 博客频道
我们在前面曾经说过,发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据包排序完成后才呈现在内核缓冲区,所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
一、粘包问题可以用下图来表示:
假设主机A send了两条消息M1和M2各10k给主机B,由于主机B一次接收的字节数是不确定的,接收方收到数据的情况可能是:
• 一次性收到20k 数据
• 分两次收到,第一次5k,第二次15k
• 分两次收到,第一次15k,第二次5k
• 分两次收到,第一次10k,第二次10k
• 分三次收到,第一次6k,第二次8k,第三次6k
• 其他任何可能
二、粘包问题的解决方案
本质上是要在应用层维护消息与消息的边界(下文的“包”可以认为是“消息”)
1、定长包
2、包尾加\r\n(ftp)
3、包头加上包体长度
4、更复杂的应用层协议
对于条目2,缺点是如果消息本身含有\r\n字符,则也分不清消息的边界。
对于条目1,即我们需要发送和接收定长包。因为TCP协议是面向流的,read和write调用的返回值往往小于参数指定的字节数。对于read调用(套接字标志为阻塞),如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用,如果请求写100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节全部交给发送缓冲区才返回。为避免这些情况干扰主程序的逻辑,确保读写我们所请求的字节数,我们实现了两个包装函数readn和writen,如下所示。
C++ Code
1 |
ssize_t readn( int fd, void *buf, size_t count) { size_t nleft = count; ssize_t nread; char *bufp = ( char *)buf; while (nleft > 0) if ((nread = read(fd, bufp, nleft)) < 0) if (errno == EINTR) else if (nread == 0) //对方关闭或者已经读到eof bufp += nread; return count; ssize_t writen(int fd, const void *buf, size_t count) while (nleft > 0) if ((nwritten = write(fd, bufp, nleft)) < 0) if (errno == EINTR) else if (nwritten == 0) bufp += nwritten; return count; } |
需要注意的是一旦在我们的客户端/服务器程序中使用了这两个函数,则每次读取和写入的大小应该是一致的,比如设置为1024个字节,但定长包的问题在于不能根据实际情况读取数据,可能会造成网络阻塞,比如现在我们只是敲入了几个字符,却还是得发送1024个字节,造成极大的空间浪费。
此时条目3是比较好的解决办法,其实也可以算是自定义的一种简单应用层协议。比如我们可以自定义一个包体结构
struct packet {
int len;
char buf[1024];
};
先接收固定的4个字节,从中得知实际数据的长度n,再调用readn 读取n个字符,这样数据包之间有了界定,且不用发送定长包浪费网络资源,是比较好的解决方案。服务器端在前面的fork程序的基础上把do_service函数更改如下:
C++ Code
1 |
void
do_service( int conn) { struct packet recvbuf; int n; while ( 1 ) { memset(&recvbuf, 0 , sizeof (recvbuf)); int ret = readn(conn, &recvbuf.len, 4 ); if (ret == - 1 ) ERR_EXIT( "read error" ); elseif (ret < 4 ) //客户端关闭 { printf( "client close\n" ); break ; } n = ntohl(recvbuf.len); fputs(recvbuf.buf, stdout); |
客户端程序的修改与上类似,不再赘述。
对于条目4,举例如 如TLV 编解码格式
struct TLV
{
uint8_t tag;
uint16_t len;
char value[0];
}__attribute__((packed));
注意value分配的是0大小,最后一个成员为可变长的数组(c99中的柔性数组),对于TLV(Type-Length-Value)形式的结构,或者其他需要变长
度的结构体,用这种方式定义最好。使用起来非常方便,创建时,malloc一段结构体大小加上可变长数据长度的空间给它,可变长部分可按数组的方式
访问,释放时,直接把整个结构体free掉就可以了。__attribute__(packed)用来强制不对struct TLV进行4字节对齐,目的是为了获取真实的TLV的
空间使用情况。
C++ Code
1 |
int main(void) |
TCP粘包问题 转自CSDN相关推荐
- TCP粘包问题分析和解决(全)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- Socket编程实践(5) --TCP粘包问题与解决
TCP粘包问题 由于TCP协议是基于字节流且无边界的传输协议, 因此很有可能产生粘包问题, 问题描述如下 对于Host A 发送的M1与M2两个各10K的数据块, Host B 接收数据的方式不确定, ...
- 【Netty入门】TCP 粘包/拆包问题产生原因
TCP粘包/分包问题的由来 因为TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送. 这样说可能比较抽象,下面举例来说明TCP拆包/粘包 ...
- 20-Netty TCP 粘包和拆包及解决方案
TCP粘包和拆包的基本介绍 TCP是面向连接的, 面向流的, 提供可靠性服务, 收发两端(客户端和服务器端) 都有一一成对的Socket,因此发送端为了将多个发给接收端的包, 更有效的发给对方, 使用 ...
- QT tcp粘包问题
QT tcp粘包问题 2016年12月27日 23:50:06 月下独奏 阅读数 1282更多 分类专栏: QT 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文 ...
- 【Netty】入门Netty官方例子解析(三)处理一个基于流的传输 TCP粘包和拆包问题分析和解决
关于 Socket Buffer的一个小警告 基于流的传输比如 TCP/IP, 接收到数据是存在 socket 接收的 buffer 中.不幸的是,基于流的传输并不是一个数据包队列,而是一个字节队列. ...
- Socket编程(4)TCP粘包问题及解决方案
① TCP是个流协议,它存在粘包问题 TCP是一个基于字节流的传输服务,"流"意味着TCP所传输的数据是没有边界的.这不同于UDP提供基于消息的传输服务,其传输的数据是有边界的.T ...
- tcp协议缓冲区溢出_关于TCP 粘包拆包,你了解吗?
一.什么是粘包拆包? 粘包拆包是TCP协议传输中一种现象概念.TCP是传输层协议,他传输的是"流"式数据,TCP并不知道传输是哪种业务数据,或者说,并不关心.它只是根据缓冲区状况将 ...
- 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)
一.粘包/拆包概念 TCP是一个"流"协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的 ...
最新文章
- 回顾 | 2018年十大AI新闻 中国继续占据主导优势
- 创建ssh 服务的镜像
- 兼容IE各版本的纯CSS二级下拉菜单
- 爱尔兰圣三一学院计算机全球排名,QS世界大学学科排名,爱尔兰圣三一学院20个学科进世界百强...
- Docker技术入门与实战 第二版-学习笔记-2-镜像构建
- wget在线扒站网站程序源码
- Ovito中多晶材料晶粒分析方法介绍
- 使用JavaVisualVM远程监控JVM虚拟机
- 机器学习面试必知:学生t分布的神奇之处
- 均线策略python代码_「Python笔记」利用Python以及Tushare实现简单的均线策略
- Unity的AB包系统使用概论
- python安装cv2模块的方法_Python opencv模块cv2安装和部分函数使用
- python制作英语字典_Python爬虫之自制英汉字典
- 有关防火墙的调研总结
- div内元素不在一行的问题解决方法
- 推荐!最新机器学习、深度学习绘图模板.ppt
- 今天是值得纪念的一天!
- 微信小程序系列(6)如何用微信小程序写一个论坛?贴心代码详解(四)搜索页
- DropWizard的AOP扩展点最佳实践
- R语言检验多重共线性 vif
热门文章
- 实施ITIL十个需要知道的事情
- 【朱-刘算法】【最小树形图】hdu6141 I am your Father!
- 解读《电力发展“十三五”规划》
- wordpress文章发布接口开发
- Measurements 和 Units,第三部分
- 《Oracle从入门到精通》读书笔记第八章 管理表空间和数据文件之二
- 2.1 . df 命令和du命令
- Spring Cloud--Honghu Cloud分布式微服务云系统—System系统管理
- [Contest20180316]Mythological IV
- Apache Ignite剖析