Netty 拆包粘包及解决方案了解吗?
1 个回答
看山,看水,看众生,也看自己。南宫博## 什么是拆包和粘包? 在TCP通信底层,当数据进入到缓冲区后,将数据进行组合发送,如果本次发送的数据超过了缓冲区的大小,那么就会将数据拆开,等到下次满足发送条件 再次发送给服务端,所以就会出现这几种情况: ### 情况一 这种属于正常情况,消息1和消息2分别发送  ### 情况二 这种就是粘包现象,消息1和消息2在一个数据保中,一起发送到服务端,服务端如果不做特殊处理是区分不出来的  ### 情况三 这种就属于拆包现象,消息1被拆成了两半,第一个数据包中只包含消息1的部分信息  ## 有哪些方案可以解决拆包和粘包呢? ### 解决方案 #### 方案一:固定长度 服务端和客户端定好每次发送的业务数据包长度,不够长度填充指定的字符,服务端每次按照固定长度进行读取 #### 方案二:分隔符 同样,服务端和客户端定好业务数据分割符,服务端接受数据时,只有当收到分隔符时,才判断分隔符之前的数据是一个完整的业务数据,否则继续等待分隔符 #### 方案三:自定义包结构体 每次发送数据的同时,将数据的长度也带上,但是存放数据长度的位置一定要定好,否则服务端无法识别哪个字节代表的是数据长度 那么 netty 针对这几种方案,自己是如何实现的呢? ### netty解决方案 注意:所有的编解码器都要放在处理器之前 #### 方案一:FixedLengthFrameDecoder 固定长度 ``` try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) //设置两个线程组 .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { log.info("客户端channel 初始化"); ch.pipeline().addLast(new FixedLengthFrameDecoder(16)); ch.pipeline().addLast(new NettyServerHandler()); } }); ChannelFuture cf = bootstrap.bind(9999).sync(); log.info("服务启动成功...."); cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess())); cf.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } ``` #### 方案二:DelimiterBasedFrameDecoder/LineBasedFrameDecoder 分隔符 ``` try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) //设置两个线程组 .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { log.info("客户端channel 初始化"); ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); ch.pipeline().addLast(new NettyServerHandler()); } }); ChannelFuture cf = bootstrap.bind(9999).sync(); log.info("服务启动成功...."); cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess())); cf.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } ``` ``` try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) //设置两个线程组 .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { log.info("客户端channel 初始化"); ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer("lglbc".getBytes()))); ch.pipeline().addLast(new NettyServerHandler()); } }); ChannelFuture cf = bootstrap.bind(9999).sync(); log.info("服务启动成功...."); cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess())); cf.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } ``` #### 方案三:自定义包结构体 ``` @Slf4j public class NettyMsgDecoder extends ByteToMessageDecoder { int length=0; @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes()>4){ if(length==0){ length=in.readInt(); } if(in.readableBytes()<length){ log.info("长度不够,继续等待"); return; } byte[] content=new byte[length]; in.readBytes(content); NettyMsg message=new NettyMsg(); message.setLen(length); message.setContent(content); // 发送给下一个handler out.add(message); length=0; } } } ``` ``` public class NettyMsgEncoder extends MessageToByteEncoder<NettyMsg> { @Override protected void encode(ChannelHandlerContext ctx, NettyMsg msg, ByteBuf out) throws Exception { out.writeInt(msg.getLen()); out.writeBytes(msg.getContent()); } } ```发布于 2025-07-17 16:28 •IP属地 青海举报评论收藏
公众号进入网站体验更佳

