Netty4.0中LengthFieldBasedFrameDecoder的使用心得
2016-02-15 13:37
387 查看
Netty4.0中LengthFieldBasedFrameDecoder的使用心得
本文中示例代码使用的是Netty4.0开发包,本人也是根据《Netty权威指南》进行的自学,若有不对的地方请大家指正。本文主要是针对遇到二进制数据包的粘包与分包问题的学习心得。
使用Netty实现客户端、服务端
若通信是全部使用Netty自带的API进行通信时,该问题都非常好解决。客户端中LengthFieldPrepender进行编码设置,不需要关心Length的大小计算,只需要按照包结构的顺序进行数据发送即可。
示例中包结构如下:
|–Length(int)–|–Txt(bytes)–|
/// client 初始化 Bootstrap bs = new Bootstrap(); bs.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipe = ch.pipeline(); pipe.addLast(new LengthFieldPrepender(4, false)); /// 从ClientHandler中获取ChannelHandlerContext txt pipe.addLast(new ClientHandler()); } }); /// client send data,不需要关心length具体赋值 String txt = "ABCDEFG_" + i; byte[] data = txt.getBytes(); ByteBuf bb = Unpooled.buffer(); bb.writeBytes(data, 0, data.length); ChannelHandlerContext ctx.writeAndFlush(bb);
服务器端采用LengthFieldBasedFrameDecoder进行解码:
ServerBootstrap b = new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 126) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.SO_REUSEADDR, true) .childOption(ChannelOption.ALLOCATOR, new PooledByteBufAllocator(false)) .childOption(ChannelOption.SO_RCVBUF, 4096) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipe = ch.pipeline(); pipe.addLast(new LengthFieldBasedFrameDecoder(30, 0, 4, 0, 4)); pipe.addLast(new StringDecoder()); pipe.addLast(new TCPServerHandler()); } });
打印结果如下:
Recv:ABCDEFG_0 Recv:ABCDEFG_1 Recv:ABCDEFG_2 Recv:ABCDEFG_3 Recv:ABCDEFG_4 Recv:ABCDEFG_5 Recv:ABCDEFG_6 Recv:ABCDEFG_7 Recv:ABCDEFG_8 Recv:ABCDEFG_9
Netty服务端、java.net.Socket客户端
上面的包结构一般不满足工程的需要,同时客户端的实现有时多种多样的,例如java.net.Socket、winsocket等。本文只是利用java.net.Socket作为客户端进行代码编写。示例中包结构如下:
|–Head Flag(short)–|–Length(int)–|–Biz Flag(long)–|–Txt(bytes)–|
上面的服务端代码中修改LengthFieldBasedFrameDecoder的参数
ChannelPipeline pipe = ch.pipeline(); pipe.addLast(new LengthFieldBasedFrameDecoder(30, 2, 4, 0, 14)); pipe.addLast(new StringDecoder()); pipe.addLast(new TCPServerHandler());
若采用客户端需要按照包结构的顺序将数据进行发送,但是length的赋值需要有技巧性,如下:
/// 数据发送时,Socket创建省略 /// prepare data String sendTxt = "ABCDEFG_"+ i; byte[] txts = sendTxt.getBytes(); int sendTxtlen = txts.length; /// send data, DataOutputStream out out.writeShort(0xabc); ///length等于bizflag+txt的字节数,而不是包的所有字节数 out.writeInt(sendTxtlen+8); out.writeLong(0xdef); out.write(txts, 0, sendTxtlen);
LengthFieldBasedFrameDecoder源码分析
//381 行,计算每frame数据的大小时,多算了length及其前面字节的长度 frameLength += lengthAdjustment + lengthFieldEndOffset; //336 行,lengthFieldEndOffset的大小计算如下 lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
相关文章推荐
- 用信号实现进程同步
- 必须掌握的八种排序(3-4)--简单选择排序,堆排序
- 【转载】网站常见的反爬虫和应对方法
- Android 开发最佳实践
- Shell简介
- wpa_supplicant —— wpa_supplicant_fd_workaround说明
- Android 快速开发系列 打造万能的ListView GridView 适配器
- IOS开发证书变成“此证书的签发者无效”解决方法
- 2014-05-26-guava-test-cases
- 第11讲项目4——玩数字
- frame,iframe,frameset用法和区别
- shell脚本之sed使用----替换、变量、转义字符
- 2014-06-06-sql-for-statistics
- arm上的一些缩略语
- ionic 滑动框图片渲染
- 在框架网页中,点击超链接,同时两个不同的FRAME中打
- LIGO 的科学家们是如何使用 Python 语言的?
- 文 雕爷 (转载)
- hdu1231 最大连续子序列(DP之最大子序列和)
- POJ 3368 区间最值