所以, 什么是粘包? 粘包:多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发生方的发送边界,而采用某一估测值大小来进行数据读出,若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包 出现粘包的原因? 出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。 先说简单的接收方原因, 接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。 再说由发送导致的粘包, 这个比较有意思. 粘包并不是 TCP 协议造成的,它的出现是因为应用层协议设计者对 TCP 协议的错误理解,忽略了 TCP 协议的定义并且缺乏设计应用层协议的经验。 如何解决粘包? 如果我们系统性地学习过 TCP 协议以及基于 TCP 的应用层协议设计,那么设计一个能够被 TCP 协议栈任意拆分和组装数据包的应用层协议就不会有什么问题。
粘包 什么是粘包? 须知:只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生 如果发生了: 1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了 我在其他地方看到两种解释: 应用程序所看到的数据是一个整体 粘包发生的两种情况: 1、发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据量很小时,会当做一个包发出去,产生粘包) 2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据 ,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前, 另一种就是将消息的长度作为消息的一部分发送出去(包头) 代码可以参考这份博客:数据粘包处理
十一、什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来 首先需要掌握一个socket收发消息的原理 ? 此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。 若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。 数据是可靠的,但是会粘包。 两种情况下会发生粘包。 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包) #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' from
# TCP粘包 # Socket通信-TCP粘包 # 什么是TCP粘包 当前消息与前一个后/后一个消息的片段一起发送到了客户端。 ? # 为什么会粘包 操作系统为了优化传输速度会将小包合并发送,大包拆分发送的情况。 由于网卡传输接口大小的限制。 # 解决办法 # 1. 固定长度分割 每次发送固定长度的消息,长度不足时补0。 ? # 总结 tcp通信时常见的难点就在于粘包的处理,只要掌握常用的处理办法就可以轻松驾驭tcp了。
粘包问题 在 TCP 这种字节流协议上做应用层分包是网络编程的基本需求。 因此,“粘包问题”是个伪命题 短连接分包 对于短连接的 TCP 服务,分包不是一个问题,只要发送方主动关闭连接,就表示一个消息发送完毕,接收方 read() 返回0,从而知道消息的结尾 TCP 发送机制
一、什么是粘包拆包? 粘包拆包是TCP协议传输中一种现象概念。TCP是传输层协议,他传输的是“流”式数据,TCP并不知道传输是哪种业务数据,或者说,并不关心。 在这个前提下,就有可能发生发生同一个业务数据被分割程多个数据包,或者多个业务数据被打包到同一个数据包进行发送。但是对于业务数据接收方,则必须拥有能够重新拆解或者组装完整业务数据的能力。 这个现象,我们称之为TCP粘包拆包。 ? 如上图,三个业务数据A、B、C被打包成一个数据包进行传输;D被分割为连个数据包进行传输。 所以综上,影响粘包拆包发生的原因: ? 关于MTU MSS相关知识可以参照:MTU(Maximum transmission unit) 最大传输单元 二、怎么处理粘包拆包? 传输层是业务无感知的,因此粘包拆包只能由业务层处理。
举个例子:客户端要发送原信息是A和B两个数据包,服务端接收到之后,可能出现如下情况: 正常情况:读取到了A和B两个数据包; 粘包:A和B两个数据包一起读取了; 拆包:读取了A数据包的一部分,A的另一部分和 B数据包一起读取了; 由于TCP是没有消息保护边界的,也就是上面的消息,没有边界,服务端并不知道hello的o是一个边界,hello是一个单词,所以我们就得中服务端处理边界问题。 这也就是粘包拆包问题。 二、Netty中的粘包拆包如何解决 使用自定义协议 + 编解码器来解决。说人话就是:服务端你不是不知道消息的长度吗? bys = msg.getBytes("utf-8"); int len = msg.getBytes("utf-8").length; // 创建协议包
如何解决粘包问题 粘包就是连续向对端发送两个或者两个以上的数据包,对端在一次收取中受到的数据包数量可能大于1个,当大于1个时,可能时几个包加上某个包的部分,这这干脆几个完整的包在一起。 当然,也可能收到的数据只是一个包的部分,这种情况一般也叫做半包。 无论是半包问题还是粘包问题,因为TCP是流式数据,所以其解决思路还是从收到的数据中把包与包的边界区分出来。 固定包长的数据包。固定包长,即每个协议包的长度都是固定的。假如我们规定每个协议包的大小都是64字节,每收满64字节,就取出来解析(如果不够,就先存起来),则这种通信协议的格式简单但灵活性太差。 以指定的字符串为包的结束标志。这种协议包比较常见,即在字节流中遇到特殊的符号值时就认为到一个包的末尾了。例如FTP或者SMTP,在一个命令或者一段数据后面加上\r\n表示一个包的结束。 由于包头的大小是固定的,这是是8字节,所以对端先收取包头大小的字节内容,然后解析包头,根据包头中指定的包体大小收取包体,等包体收够了,就组装成一个完整的包来处理。
这就是粘包和半包。 那么,什么是粘包和半包? 粘包和半包定义如下: 粘包和半包,指的都不是一次是正常的 ByteBuf 缓存区接收。 粘包,就是接收端读取的时候,多个发送过来的 ByteBuf “粘”在了一起。 换句话说,接收端读取一次的 ByteBuf ,读到了多个发送端的 ByteBuf ,是为粘包。 粘包和半包 图解 上面的理论比较抽象,下面用一幅图来形象说明。 下图中,发送端发出4个数据包,接受端也接受到了4个数据包。但是,通讯过程中,接收端出现了 粘包和半包。 粘包和半包更全实验 上面的实例,只能看到半包的结果,看不到粘包的结果。 为了看到粘包的场景,这里,不使用protobuf 协议,直接使用缓冲区进行读写通讯,设计了一个的简单的演示实验案例。 运行实例,不仅可以看到半包的提示信息输出,而且可以看到粘包的提示信息输出,示意图如下: 我们可以看到,服务器收到的数据包,有包含多个发送端数据包的,这就是粘包了。
然而在接收端,数据可能以不同的方式到达,就比如正常包、粘包、拆包。 ; 情况2: 服务器只接收到一个数据包(存在粘包问题) 因为tcp不会丢失数据包,因此这一个数据包就封装了2个原生数据包的信息,这种现象叫做粘包。 在这种情况下,粘包与拆包同时发生。 ) 如果无法还原,则服务器无法正确解析报文并做相应处理; 3、粘包与拆包主要原因 1、粘包原因: 发送的数据大小 小于 发送缓冲区,TCP就会把发送的数据多次写入缓冲区,此时发生粘包; 接收数据方的应用层没有及时从 接收缓冲区读取数据,也会发生粘包; 2、拆包原因: 发送的数据大小 大于 TCP发送缓冲区,就会发生拆包; 发送的数据大小 大于 报文最大长度,也会拆包; 4、粘包与拆包解决方法 解决粘包拆包的关键在于
tcp 长链接模式下,使用固定消息头长度的方式进行消息 拆包 ,解决 粘包 问题。 组包 <? $bar; 粘包 // send // 传输 $package 由 $foo $bar 两条消息组成 模拟粘包场景 // receive 拆包 <? php // 解析第1条消息 取前 2bytes 按 n 解包 $fooLen = unpack("n", substr($package, 0, 2))[1]; // 使用包消息体长度定义读取消息体 但如果是 短连接多个消息 或 长链接模式 下,就可能会发生粘包,客户端不关闭服务端无法通过 EOL 确定消息读取完毕的问题。这就需要定义协议和拆包。
一、何为TCP粘包/拆包? TCP会根据缓冲区的实际大小情况进行包的拆分和合并,所谓粘包,就是将多个小的包封装成一个大的包进行发送。拆包,即是将一个超过缓冲区可用大小的包拆分成多个包进行发送。 二、粘包/拆包产生的原因 1、写入的字节大小大于套接字的发送缓存区大小。 、将消息分成消息头和消息体两部分,消息头记录的消息的总长度 四、未考虑TCP粘包/拆包的案例 服务端: public class Server { private int port; 五、加入Netty的TCP粘包/拆包解决方案。 Netty解决TCP粘包/拆包相关类以及功能: 1、LineBasedFrameDecoder:以\r或\r\n为分隔符 2、StringDecoder:将接收到的消息转换成字符串 3、DelimiterBasedFrameDecoder
粘包现象 客户端在一个for循环内连续发送1000个hello给Netty服务器端, 1 Socket socket = new Socket("127.0.0.1", 10101) 这种现象我们称之为粘包. ? 为什么会出现这种现象呢? TCP是个”流”协议,流其实就是没有界限的一串数据。 TCP底层中并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包划分, 所以在TCP中就有可能一个完整地包会被TCP拆分成多个包,也有可能吧多个小的包封装成一个大的数据包发送。 一个简单的处理逻辑是在发送数据包之前, 先用四个字节占位, 表示数据包的长度. 因此我们自定义数据包里面, 不仅要引入数据包长度, 还要引入一个包头来划分各个包的范围.
消息在进入传输层(TCP)时会被切片为一个个数据包。这个数据包的长度是MSS。 上面说的其实就是TCP的 Nagle 算法优化,目的是为了避免发送小的数据包。 在 Nagle 算法开启的状态下,数据包在以下两个情况会被发送: 如果包长度达到MSS(或含有Fin包),立刻发送,否则等待下一个包到来;如果下一包到来后两个包的总长度超过MSS的话,就会进行拆分发送; 等待超时(一般为200ms),第一个包没到MSS长度,但是又迟迟等不到第二个包的到来,则立即发送。 因此 UDP 头的这个长度其实跟 TCP 为了防止粘包而在消息体里加入的边界信息是起一样的作用的。
Netty如何解决TCP粘包拆包的问题? TCP粘包/拆包 TCP协议是个流协议,所谓流,就是指没有界限的一串数据。河里的流水,是连成一片的,没有分界线。 TCP粘包和拆包问题。 粘包拆包说明 现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下: 第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象 第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。 3.要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。 4.接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。
TCP粘包的原因和表现TCP粘包指的是发送方在发送数据时,将多个逻辑上独立的数据包粘合在一起发送,导致接收方在接收时无法正确地区分这些数据包。 造成TCP粘包的原因有多种,包括网络传输的延迟、缓冲区的限制、发送方的发送策略等。TCP粘包的表现形式有两种:多个数据包粘合在一起,形成一个大的数据包。 一个数据包被拆分成多个小的数据包,但是在接收端可以正确地解析出每个数据包。TCP粘包、拆包的解决方式为了解决TCP粘包、拆包的问题,我们可以采用以下几种方式:1. 一个数据包被拆分成多个小的数据包进行传输,但是接收方无法正确地组装这些数据包。解决TCP粘包、拆包的方式为了解决TCP粘包和拆包的问题,我们可以采取以下几种方式:1. 消息头部包含长度信息一种更为常见的解决TCP粘包和拆包问题的方式是在消息头部包含长度信息。
这就是 TCP 协议的粘包/拆包概念。 Netty 粘包/拆包问题 为突出 Netty 的粘包/拆包问题,这里通过例子进行重现问题,以下为突出问题的主要代码: 服务端: /** * 服务端网络事件的读写操作类 * * Created by 如上图所示, 【】中的最后一个数字与 []中数字对上的是已独立完整的包接收到(粘包/拆包示意图中的情况 I)。 但是 【】中为 37和 38的出现了粘包情况(粘包/拆包示意图中的情况 II),两条数据粘合在一起。 Netty 解决粘包/拆包问题 LineBasedFrameDecoder 换行符处理 Netty 的强大,方便,简单使用的优势,在粘包/拆包问题上也提供了多种编解码解决方案,并且很容易理解和掌握。
这就是 TCP 协议的粘包/拆包概念。 本文基于 Netty5 进行分析 粘包/拆包描述 假设当前有 123和 abc两个数据包,那么他们传输情况示意图如下: ? Netty 粘包/拆包问题 为突出 Netty 的粘包/拆包问题,这里通过例子进行重现问题,以下为突出问题的主要代码: 服务端: /** * 服务端网络事件的读写操作类 * * Created by 如上图所示, 【】中的最后一个数字与 []中数字对上的是已独立完整的包接收到(粘包/拆包示意图中的情况 I)。 但是 【】中为 37和 38的出现了粘包情况(粘包/拆包示意图中的情况 II),两条数据粘合在一起。 ? Netty 解决粘包/拆包问题 LineBasedFrameDecoder 换行符处理 Netty 的强大,方便,简单使用的优势,在粘包/拆包问题上也提供了多种编解码解决方案,并且很容易理解和掌握。
四、使用包定长FixedLengthFrameDecoder解决半包粘包 4.1 试验 由于客户端发给服务器端的是hello server,im a client字符串,该字符串占用24字节,所以在服务器端 hello client ,im server 9receive from server:hello client ,im server 可知使用FixedLengthFrameDecoder已经解决了半包粘包问题 4.2 FixedLengthFrameDecoder的原理 顾名思义是使用包定长方式来解决粘包半包问题,假设服务端接受到下面四个包分片: ? 代码4.2.4.4判断是否只需要读取单个包(默认false),如果是则读取一个包后就跳出循环,也就是如果出现了粘包现象,在一次channelRead事件到来后并不会循环读取所有的包,而是读取最先到的一个包 最后 想了解JDK NIO和更多Netty基础的可以单击我 想了解更多关于粘包半包问题单击我 更多关于分布式系统中服务降级策略的知识可以单击 单击我 想系统学dubbo的单击我 想学并发的童鞋可以
入门篇 高性能NIO框架Netty-对象传输 高性能NIO框架Netty-整合kryo高性能数据传输 高性能NIO框架Netty-整合Protobuf高性能数据传输 Netty4自带编解码器详解 TCP黏包拆包 TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送 ,这就是所谓的TCP粘包和拆包问题。 DelimiterBasedFrameDecoder(特殊分隔符分包) FixedLengthFrameDecoder(固定长度报文来分包) LengthFieldBasedFrameDecoder(自定义长度来分包) 制造粘包和拆包问题 为了验证我们的解码器能够解决这种粘包和拆包带来的问题,首先我们就制造一个这样的问题,以此用来做对比。