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來消息頭和消息體來解決粘包和半包問題。