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<SocketChannel>() {
@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<SocketChannel>() {
@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来消息头和消息体来解决粘包和半包问题。