netty中的粘包和拆包问题处理
来源:互联网 发布:租用临时备案域名 编辑:程序博客网 时间:2024/06/05 06:15
1、tcp粘包和拆包问题说明
我们可以通过下图来说明tcp粘包和拆包问题的说明:
假设客户端发送d1和d2两个数据包到服务端,此时服务端收到包有如下几种情况:
1:服务端正常收到d1和d2两个数据包;
2:服务端收到一个数据包即是D1和D2粘在一起,此时称为粘包;
3:服务端分2次收到2个数据包,第一次是d1的完整包和d2的部分包,第二次是d2剩余的包,此时就是拆包;
4:服务端分2次收到2个数据包,第一次是d1的部分包,第二次是d1的剩余包和d2的包。
还有一种情况是数据包较大,但tcp的接收窗口非常小,此时服务端要分多次才能收完D1和D2的数据包。
2、tcp粘包和拆包发生的原因
1、应用程序write写入的字节大小大于套接口发送缓冲区大小;
2、进行Mss大小的tcp分段;
3、以太网贞的payload大于MTU进行ip分片。
3、粘包问题的解决策略
由于底层的tcp无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计解决。根据业界的主流协议的解决方案,可以归纳如下:
1)消息定长,例如每个报文大小长度200字节,如果不够,空位补空格;
2)在包尾增加回车换行进行分割,例如ftp协议;
3)将消息分为消息头和消息体,消息头包含表示消息总长度的字段,通常设计思路为消息头的第一字段使用int32来 表示消息的总长度;
4)更复杂的应用层协议;
4、tcp粘包异常案例
这里改造下timeServer,在收到消息的时候去除回车换行符。
package zou;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;public class TimeServerHandler extends ChannelHandlerAdapter {private int counter;@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//读取客户端发送的字节ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length());//String body = (String) msg;System.out.println("the time server receive order :" + body + "the counter:" + ++counter);String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";currentTime += System.getProperty("line.separator");ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());//进行发送消息到客户端ctx.writeAndFlush(resp);}//@Override//public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {////通过调用此方法,将发送的缓冲区的消息全部写到SocketChannel中//ctx.flush();//}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}timeClientHandler如下:
public class TimeClientHandle extends ChannelHandlerAdapter {private static final Logger logger = Logger.getLogger(TimeClientHandle.class.getName());private int counter;private byte[] req;public TimeClientHandle() {req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();}//连接成功后发送指令@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ByteBuf message = null;for (int i = 0; i < 100; i++) {message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, "utf-8");//String body = (String) msg;System.out.println("Now is:" + body + "the counter is " + ++counter);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}这里会进行发送100次查询时间的消息,按道理服务端和客户端都应该有100次的显示,但执行后结果如下:
the time server receive order :QUERY TIME ORDER
QUERY TIME ORDER
。。。。。
the time server receive order :Y TIME ORDER
QUERY TIME ORDER
。。。。。
the time server receive order :Y TIME ORDER
客户端执行如下:
Now is:BAD ORDER
BAD ORDER
the counter is 1
BAD ORDER
the counter is 1
此时说明已经产生了粘包。
5、利用LineBaseFrameDecoder解决tcp粘包问题
为了解决tcp的粘包/拆包问题,netty默认提供了多种编码解码器用于处理半包。只要熟练掌握这些类库的使用,tcp粘包问题从此就会变得非常容易。调整点如下,TimeServer进行handler的初始化如下:
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel arg0) throws Exception {arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));arg0.pipeline().addLast(new StringDecoder());arg0.pipeline().addLast(new TimeServerHandler());}}这里添加了2个编码器,lineBaseFrameDecode和StringDecoder,后面的TimeServerHandler 读取消息调整如下:
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//读取客户端发送的字节String body = (String) msg;System.out.println("the time server receive order :" + body + "the counter:" + ++counter);String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";currentTime += System.getProperty("line.separator");ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());//进行发送消息到客户端ctx.writeAndFlush(resp);}此时代码很简洁,直接将消息转换为字符串,且已经是去除回车换行的。TimeClient的调整如下,也是增加解码器:
protected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LineBasedFrameDecoder(1024));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new TimeClientHandle());}其timeClientHandler进行解码如下:
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String body = (String) msg;System.out.println("Now is:" + body + "the counter is " + ++counter);}
非常简洁。相对于之前的调整有增加了解码器,另外在进行读取消息时候代码也简洁了很多,不用编码解码。
进行运行发现没有产生tcp粘包的问题。
6、LineBaseFrameDecoder和StringDecoder原理分析
LineBaseFrameDecoder的原理就是依次遍历ByteBuf中的可读字节,判断是否有“\n”或者是“\r\n”,如果有就以此为换行。另外它也能指定长度,如果在指定的长度内没有换行符那么就会抛出异常。
StringDecoder非常简单,就是将接收到对象转换为字符串,然后继续后面的Handler调用。LineBaseFrameDecoder和StringDecoder结合起来使用就是按行切换的文本解码器,它被设计用来支持tcp的粘包和拆包问题。
阅读全文
0 0
- netty中的粘包和拆包问题处理
- netty 粘包问题处理
- Netty的入门-拆包和粘包的处理
- Netty中处理TCP粘包和拆包
- Netty自定义协议的粘包和拆包处理
- Netty中处理TCP粘包和拆包
- Netty(三):网络传输中的拆包、粘包问题
- netty解决TCP网络传输中的拆包与粘包问题
- Netty初探-解决TCP粘包/拆包问题
- 聊一聊Netty TCP粘包/拆包问题的解决办法
- Netty实践(二):TCP拆包、粘包问题
- netty学习笔记(2)_tcp拆包/粘包问题
- 【Netty入门】TCP 粘包/拆包问题产生原因
- netty的粘包 解包问题
- netty处理粘包问题——1
- netty处理粘包问题——2
- java netty使用DelimiterBasedFrameDecoder处理tcp粘包问题
- Netty权威指南之TCP粘包和拆包
- 【Unity优化】GPU优化
- NZT 更新 通用 办法
- tieba.py
- 算法课第16周第1题—— 215. Kth Largest Element in an Array
- MY SQL中''与null的区别
- netty中的粘包和拆包问题处理
- 使用Mweb生成博客到csdn
- HDU5776 sum【前缀和+模除】
- 【Unity优化】Shader优化
- 38 WebGL针对单独的顶点坐标绘制组成模型
- Ubuntu14.04上安装ROS
- 后端框架_数据库技术mysql
- HashSet哈希值
- 字符数组、String类、StringBuffer三者的相互转换