这里介绍两种解决办法:1、利用LengthFieldBasedFrameDecoder解码器 2、设置自定义编解码。
一般出现数据粘包问题很难复现,因为在开发中,我们都是模拟发数据,真正上线了,我们的数据发送就没有什么规律,可能很快,也可能慢,频率不同,难以捕捉问题,但是肯定会出现一些粘包问题。
所谓粘包,就是接收的数据,无法分开,一个包的数据可能是两个或者多个报文记录,我们需要严格按照报文格式将这些合在一起的包拆开。思路就是要么报文有严格的分隔符,要么定长,再就是自定义解码器或者利用netty自带的LengthFieldBasedFrameDecoder解码器。
前面说过,粘包问题在实际中很难复现,这对于我们查找问题不是很方便,但是我这里通过自己编写的client发送报文,几乎可以100%的复现。
这个客户端,就是前面两篇博客中提到的c语言自定义tcp协议实现socket通信的客户端。
netty实现的tcp server代码如下所示:
package com.xxx.huali.hualitest.nettyio;
import java.nio.ByteOrder;
import com.xxx.huali.hualitest.nettyio.decode.MyDecoder;
import com.xxx.huali.hualitest.nettyio.decode.MyHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public class ServerApp {
private final static int port = 6666;
public static void main(String[] args) {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 65535, 8 , 4, 1, 0, true));
//ch.pipeline().addLast("decoder",new MyDecoder());
ch.pipeline().addLast(new ServerHandler());
//ch.pipeline().addLast("handler", new MyHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
}
class ServerHandler extends SimpleChannelInboundHandler<ByteBuf>{
int count = 1;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
// TODO Auto-generated method stub
// flag version type reserved length payload checksum
// 2 2 2 2 4 length 1