此文僅用作備忘
原文鏈接:https://blog.csdn.net/q56231293811/article/details/78741780
1、問題概述
tcp產生粘包問題的原因有
- 應用程序write寫入的字節大小大於套接字發送緩沖區的大小。
- 進行MSS(TCP的數據部分)大小的TCP分段。
- 以太網幀的payload大於MTU進行IP分片
業界解決方法
tcp粘包的問題只能通過上層的應用協議棧來設計解決,根據業界的主流協議的解決方案,可以歸納如下。
- 消息定長,例如每個報文的大小固定,例如固定為100字節,如果長度不夠,可以用空白填充。
- 在包尾增加回車換行符進行分割,例如FTP協議。
- 將消息分為消息頭和消息體,消息頭中包含表示消息的總長度(或者消息體長度)的字段,通常設計思路為消息頭的第一個字段使用int32來表示消息的總長度
netty粘包處理
netty提供了多種編碼器用於處理半包,這些編碼器包含
- LineBasedFrameDecoder 時間解碼器
- DelimiterBasedFrameDecoder 分隔符解碼器
- FixedLengthFrameDecoder 定長解碼器
這里介紹使用DelimiterBasedFrameDecoder 來處理包過長,發送的測試數據如下:
don't know what I do now is right, those are wrong, and when I finally Laosi when I know these. So I can do now is to try to do well in everything, and then wait to die a natural death.
Sometimes I can be very happy to talk to everyone, can be very presumptuous, but no one knows, it is but very deliberatelycamouflage, camouflage; I can make him very happy very happy,
but couldn't find the source of happiness, just giggle If not to the sun for smiling, warm is still in the sun there, but wewill laugh more confident calm; if turned to found his own shadow,
appropriate escape, the sun will be through the heart,warm each place behind the corner; if an outstretched palm cannot fall butterfly, then clenched waving arms, given power; if I can't
have bright smile, it will face to the sunshine, and sunshine smile together, in full bloom. Time is like a river, the left bank is unable to forget the memories, right is worth grasp the
youth, the middle of the fast flowing, is the sad young faint. There are many good things, buttruly belong to own but not much. See the courthouse blossom,honor or disgrace not Jing, hope
heaven Yunjuanyunshu, has no intention to stay. In this round the world, all can learn to use a normal heart to treat all around, is also a kind of realm!
這里的數據超過1024字節的長度,正常情況下這個tcp會產生粘包或者拆包現在,結果如圖所示:
可以看出這段數據被拆成了兩斷,下面使用DelimiterBasedFrameDecoder 處理這個問題,處理數據的源碼如下所示:
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.UnsupportedEncodingException; public class TimeServerHandler extends ChannelInboundHandlerAdapter { private int counter; //有連接可以讀取 @Override public void channelRead(ChannelHandlerContext ctx,Object msg){ String body = (String) msg; try { System.out.println("The time server receive order :" +(++counter)+":"+ body); String currentTime = System.currentTimeMillis()+""; ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.write(resp); } catch (Exception e) { e.printStackTrace(); } } @Override public void channelReadComplete(ChannelHandlerContext ctx){ ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){ System.out.println(cause.toString()); ctx.close(); } }
啟動初始化類如下:
import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class TimeServer { public void bind(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try{ ServerBootstrap b = new ServerBootstrap(); //最大連接數量設定 BACKLOG用於構造服務端套接字ServerSocket對象,標識當服務器請求處理線程全滿時,用於臨時存放已完成三次握手的請求的隊列的最大長度。如果未設置或所設置的值小於1,Java將使用默認值50。 b.group(bossGroup,workGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024).childHandler(new ChildChannelHandler()); //綁定端口,等待同步成功 ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel> { protected void initChannel(SocketChannel socketChannel) throws Exception { ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes()); socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(2048,delimiter)); socketChannel.pipeline().addLast(new StringDecoder()); socketChannel.pipeline().addLast(new TimeServerHandler()); } } public static void main(String[] args){ int port = 9000; try { new TimeServer().bind(port); } catch (Exception e) { e.printStackTrace(); } } }
在測試數據最后加上$_$,再次使用網絡助手進行測試,如下圖所示,tcp粘包問題得以解決
————————————————
版權聲明:本文為CSDN博主「羊掌門」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/q56231293811/article/details/78741780