您的位置:首页 > 其它

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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: