正常流程:TCP缓存->Netty本地缓存->拆包器拆包->Handler处理封装好的数据包
测试代码:netty/demo/tcppackage
参考博文:http://www.jianshu.com/p/a0a51fd79f62
项目地址
测试流程:
@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数据长度:18
channelReadComplete1
channelRead数据长度:18
channelReadComplete2
channelRead数据长度:18
channelReadComplete3
channelRead数据长度:18
channelReadComplete4
channelRead数据长度:18
channelReadComplete5
channelRead数据长度:18
channelReadComplete6
channelRead数据长度:18
channelReadComplete7
对比连续发送110 次的结果
channelRead数据长度:1024
channelRead数据长度:956
channelReadComplete2
对比结果可以猜测数据流连续和不连续会有不同的结果
当发送端发送数据过快,channelReadComplete可能会很久才被调用一次
如果一次数据读取完毕之后(可能接收端一边收,发送端一边发,这里的读取完毕指的是接收端在某个时间不再接受到数据为止) 至少在1s已经超过了这个时间
arg0.pipeline().addLast(new StringDecoder());
连续发送110次的结果
还是读了两次:channelReadComplete 2
唯一的区别是 msg 直接变成了 String类型
基于换行符的拆包器,最大长度为1024
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
连续发送110次结果
数据内容:Query time order
channelRead数据长度:16
数据内容:Query time order
channelRead数据长度:16
数据内容:Query time order
channelRead数据长度:16
数据内容:Query time order
channelRead数据长度:16
数据内容:Query time order
channelRead数据长度:16
数据内容:Query time order
channelReadComplete110
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 3
channelReadComplete2
结论:自动丢包
可读字节长度:102 > 大于拆包器 最大长度20
102个字节自动进入丢包模式
丢包的原则:参考LineBasedFrameDecoder.java 源码