Netty 拆包 丢包 过程分析
来源:互联网 发布:中国流动人口数据 编辑:程序博客网 时间:2024/05/16 05:04
基础
正常流程:TCP缓存->Netty本地缓存->拆包器拆包->Handler处理封装好的数据包
测试代码:netty/demo/tcppackage
参考博文:http://www.jianshu.com/p/a0a51fd79f62
项目地址
如果不设置解码器
测试流程:
- 客户端连续发送n次18字节的数据
@Override public void channelActive(ChannelHandlerContext ctx) { ByteBuf message = null; //连续发送100个包 56 一次 113两次 170 收三次 for (int i = 0; i < 110; i++) { message = Unpooled.buffer(req.length); message.writeBytes(req); ctx.writeAndFlush(message); } }
服务端在channelRead 回调函数中处理接收的数据
初始化本地缓存大小[1024] 1kb
.option(ChannelOption.SO_BACKLOG, 1024)
加断点测试:
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {[breakpoint] ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); }
- 测试结果
n = 56
channelRead 只执行 1 次
buf 数据长度为: 1008
buf 容量为: 1024
n = 110
channelRead 执行 2 次
第一次
buf 数据长度为: 1024
buf 容量为: 1024
第二次
buf 数据长度为: 956
buf 容量为: 1024
- 结论
channelRead的回调次数与发送端发送的数据有关
次数 = 数据总量/本地缓存size + 1
最后一次可以不满 1024
- 疑惑
接收端是怎么知道发送端的数据发完了的呢?
writeAndFlush 是将数据发送出去
以上的数据是连续的发送110次本地缓存会满1024,
如果不连续的发送会怎样呢?
隔1s 发送一次
@Override public void channelActive(ChannelHandlerContext ctx) { ByteBuf message = null; //连续发送100个包 56 一次 113两次 170 收三次 for (int i = 0; i < 110; i++) { message = Unpooled.buffer(req.length); message.writeBytes(req); ctx.writeAndFlush(message); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
服务端接收结果:
channelRead数据长度:18channelReadComplete1channelRead数据长度:18channelReadComplete2channelRead数据长度:18channelReadComplete3channelRead数据长度:18channelReadComplete4channelRead数据长度:18channelReadComplete5channelRead数据长度:18channelReadComplete6channelRead数据长度:18channelReadComplete7
对比连续发送110 次的结果
channelRead数据长度:1024channelRead数据长度:956channelReadComplete2
对比结果可以猜测数据流连续和不连续会有不同的结果
当发送端发送数据过快,channelReadComplete可能会很久才被调用一次
如果一次数据读取完毕之后(可能接收端一边收,发送端一边发,这里的读取完毕指的是接收端在某个时间不再接受到数据为止) 至少在1s已经超过了这个时间
设置解码器和编码器
- 给接收端添加一个字符串解码器
arg0.pipeline().addLast(new StringDecoder());
连续发送110次的结果
还是读了两次:channelReadComplete 2唯一的区别是 msg 直接变成了 String类型
- 在添加了字符串解码器的基础上添加一个拆包器
基于换行符的拆包器,最大长度为1024
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
连续发送110次结果
数据内容:Query time orderchannelRead数据长度:16数据内容:Query time orderchannelRead数据长度:16数据内容:Query time orderchannelRead数据长度:16数据内容:Query time orderchannelRead数据长度:16数据内容:Query time orderchannelRead数据长度:16数据内容:Query time orderchannelReadComplete110
- 结论
channelRead 接收的数据包的长度不是固定的1024了;
而是 分隔符 分割出来的数据包
回调中获取的字符串 不包含分隔符(2字节)
- 疑惑
如果一行数据超过了最大帧长会怎样
将发送端和接收端的最大帧长设置为10
An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.io.netty.handler.codec.TooLongFrameException: frame length (68) exceeds the allowed maximum (10)需要拆包的长度 是否大于该拆包器允许的最大长度(maxLength),这个参数在构造函数中被传递进来,如超出允许的最大长度,就将这段数据抛弃,返回null;
数据长度(需要拆包的长度)是68 但是帧长(拆包器最大长度)10,这段数据被抛弃
将接收端的最大帧长调整为 68 正常;调整值小于68就异常了
将发送端的帧长设置为10对发送没有影响
如果一部分数据在拆包器允许的最大范围内,一部分超过了正常的,如何丢包?
修改接收端的最大帧长:20
arg0.pipeline().addLast(new LineBasedFrameDecoder(20));
让发送端连发三波数据:
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); ByteBuf message = null;// ========第一波数据================== String msg1 = "Query time order 1"; message = Unpooled.buffer((msg1+ System.getProperty("line.separator")).getBytes().length); message.writeBytes((msg1 + System.getProperty("line.separator")).getBytes()); ctx.writeAndFlush(message);// ========第二波数据=================== StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 6; i++) { stringBuilder.append("Query time order2"); } String msg2 = stringBuilder.toString(); message = Unpooled.buffer((msg2+ System.getProperty("line.separator")).getBytes().length); message.writeBytes((msg2 + System.getProperty("line.separator")).getBytes()); ctx.writeAndFlush(message);// =========第三波数据===================== String msg3 = "Query time order 3"; message = Unpooled.buffer((msg3+ System.getProperty("line.separator")).getBytes().length); message.writeBytes((msg3 + System.getProperty("line.separator")).getBytes()); ctx.writeAndFlush(message); }
测试结果:
channelRead数据长度:18数据内容:Query time order 1六月 29, 2017 2:10:11 下午 io.netty.channel.DefaultChannelPipeline onUnhandledInboundException警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.io.netty.handler.codec.TooLongFrameException: frame length (102) exceeds the allowed maximum (20)channelRead数据长度:18数据内容:Query time order 3channelReadComplete2
结论:自动丢包
可读字节长度:102 > 大于拆包器 最大长度20
102个字节自动进入丢包模式
丢包的原则:参考LineBasedFrameDecoder.java 源码
- Netty 拆包 丢包 过程分析
- netty源码分析(二十四)TCP粘包与拆包实例演示及分析
- Netty -- TCP粘包/拆包
- netty拆包/粘包的解决方案
- Netty 粘包、拆包解决示例
- Netty粘包/拆包支持
- Netty初探-解决TCP粘包/拆包问题
- Netty权威指南之TCP粘包和拆包
- Netty学习之TCP粘包/拆包
- Netty的入门-拆包和粘包的处理
- Netty中处理TCP粘包和拆包
- 聊一聊Netty TCP粘包/拆包问题的解决办法
- Netty (三) TCP粘包和拆包解决方案
- Netty学习10-粘包和拆包
- Netty实践(二):TCP拆包、粘包问题
- netty中TCP的黏包/拆包解决之道
- Netty自定义协议的粘包和拆包处理
- Netty学习(四)-TCP粘包和拆包
- 本人亲身讲解本科期间学习Linux系统过程
- phpstorm安装laravel-ide-helper实现自动完成、代码提示和跟踪
- 代码日常--如何在SQL查询语句中添加判断逻辑
- CPP_Basic_Code_P12.1-PP12.10.4
- 使用virtualbox和gdb调试内核
- Netty 拆包 丢包 过程分析
- 为什么JavaScript是单线程?
- 方差、标准差、均方差、均方误差、均方根误差、标准误差
- JDBC
- STM32 输入捕获的脉冲宽度及频率计算
- 重载和重写
- linux下通过C语言读取BMP格式图片,在文本终端显示该图片
- POJ2536 Gopher II(二分图最大匹配)
- 关于解决No service of type Factory available in ProjectScopeServices的错误