TCP是一个基于“流”的协议,所谓流就像河里的水没有严格的界限。TCP底层并不了解上层业务的具体实现,它会根据TCP缓冲区的具体情况进行划分,所以就可能会出现业务上一个完整的TCP包可能会拆分成多个小包进行发送,也可能多个小包被组装成一个大包进行发送,从而导致了TCP中的粘包和半包问题。
解决方案
- 消息定长,每发送一次消息,在接收消息的同时截取固定长度的字节。
- 以某种分隔符进行分割。
- 把消息封装成消息头和消息体。
netty中的粘包和半包解决方案
消息定长格式解决
通过添加DelimiterBasedFrameDecoder来解决粘包半包问题
b.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .handler(new LoggingHandler()) .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); //socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(10)); socketChannel.pipeline().addLast(new StringDecoder()); socketChannel.pipeline().addLast(new EchoServerHandler()); } });
根据特定字符分割解决
通过添加FixedLengthFrameDecoder获取固定长度字符来解决粘包半包问题
b.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .handler(new LoggingHandler()) .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); //socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(10)); socketChannel.pipeline().addLast(new StringDecoder()); socketChannel.pipeline().addLast(new EchoServerHandler()); } });
根据消息头和消息体封装解决
通过ObjectDecoder和ObjectEncoder来消息头和消息体来解决粘包和半包问题。