NETTY学习之粘包、半包

本文深入探讨Netty中粘包与半包问题的成因及解决方案,讲解了固定长度、分隔符和长度字段等常见解码方法,并通过实例代码展示了如何使用ByteToMessageDecoder实现自定义解码器。
部署运行你感兴趣的模型镜像

前言:

netty学习的第一篇文章,讲讲内容是啥。最近在极客时间上学netty相关的课,老师上课方式很有问题,每节课先抛出几个问题,在之后的内容中解答这几个问题。而我的话,在看完课之后,再拿着问题,自己解答一遍,这样学习的效率很不错。所以接下来的几篇文章就都是我的学习总结了。第一篇是粘包、半包。

在这里插入图片描述

粘包:一个包里发了多个数据

原因:
1.写入的数据<缓冲区大小(缓冲区没满,就不发)
2.接收方从缓冲区读取的速度太慢

半包:一个包里只有半条数据

原因:
1。写入的数据>缓冲区大小
2.写入的数据大于协议的MTU(最大传输单元),必须拆包

解决粘包和半包的常用方法:

1.固定长度
2.设置分隔符
3.固定长度字段存内容的长度信息(和现在的协议里很像,先给个长度,然后根据长度取截取)

在这里插入图片描述

粘包、半包的解决方式:

1.解码核心工作流程
先说:
维护一个数据积累器,根据不同的解码方式,进行解码

* For example here is an implementation which reads all readable bytes from
* the input {@link ByteBuf} and create a new {@link ByteBuf}.
*
* <pre>
*     public class SquareDecoder extends {@link ByteToMessageDecoder} {
*         {@code @Override}
*         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, List&lt;Object&gt; out)
*                 throws {@link Exception} {
*             out.add(in.readBytes(in.readableBytes()));
*         }
*     }
* </pre>

通过创建一个decoder继承byteToMessageDecoder

final ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
        // 组装 NioEventLoopGroup
        .group(bossGroup, workerGroup)
        // 设置channel类型为 NIO 类型
        .channel(NioServerSocketChannel.class)
        // 设置连接配置参数 服务端可连接队列数,对应 TCP/IP 协议 listen 函数中 backlog 参数
        .option(ChannelOption.SO_BACKLOG, 1024)
        // 长连接
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        // 立即写出
        .childOption(ChannelOption.TCP_NODELAY, true)
        // 配置入站、出站事件handler
        .handler(new LoggingHandler(LogLevel.DEBUG))
        .childHandler(gbt32960ChannelInitializer);

先创建serverBootstrap
然后加载childHandler

GBT32960ChannelInitializer extends ChannelInitializer<NioSocketChannel> 

 @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) {
        ChannelPipeline pipeline = nioSocketChannel.pipeline();
        // 心跳检测
        pipeline.addLast("idleStateHandler",
                new IdleStateHandler(readTimeOut, 0, 0, TimeUnit.MINUTES));
        // 分隔符解码器配置 0x23 0x23
//        pipeline.addLast(new DelimiterBasedFrameDecoder(
//                1100, Unpooled.copiedBuffer(new byte[]{DELIMITER_GBT32960_23, DELIMITER_GBT32960_23})));
        pipeline.addLast(new GBT32960BaseDecoder());
        pipeline.addLast(new GBT32960Decoder());
        pipeline.addLast(new GBT32960Encoder());
        pipeline.addLast(businessGroup, gbt32960ClientRealDataMsgHandler);
        pipeline.addLast(businessGroup, gbt32960ClientLoginMsgHandler);
        pipeline.addLast(businessGroup, gbt32960ClientLogoutMsgHandler);
        pipeline.addLast(businessGroup, gbt32960DataPacketHandler);
    }

pipeLine里面添加decoder,继承byteToMessageDecoder,重写其decode方法。
GBT32960BaseDecoder extends ByteToMessageDecoder {

在这里插入图片描述

如果要用默认的decoder的话,直接
pipeline.addLast(new FixedLengthFrameDecoder());

提供几个粘包半包的解决方式。上面有一堆提供的。(这里部分是目前项目中根据业务自己创建的decode方法)

您可能感兴趣的与本文相关的镜像

Seed-Coder-8B-Base

Seed-Coder-8B-Base

文本生成
Seed-Coder

Seed-Coder是一个功能强大、透明、参数高效的 8B 级开源代码模型系列,包括基础变体、指导变体和推理变体,由字节团队开源

### Java Netty 中处理半包问题的解决方案 在 Java Netty 中,半包问题是由于 TCP 协议的特性导致的。TCP 是一种面向字节流的协议,它并不关心上层应用发送的数据的实际边界[^3]。因此,接收方可能会接收到不完整的消息(半包)或多条消息的组合()。为了解决这一问题,Netty 提供了多种解码器来处理这些情况。 #### 1. 使用 `LengthFieldBasedFrameDecoder` `LengthFieldBasedFrameDecoder` 是 Netty 提供的一个基于长度字段的解码器。它可以解析含长度字段的消息帧,并确保接收到完整的消息。以下是一个示例代码: ```java package com.example.netty.nian; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class TestLengthFieldDecoder { public static void main(String[] args) { EmbeddedChannel channel = new EmbeddedChannel( new LengthFieldBasedFrameDecoder(1024, 0, 4, 1, 0), new LoggingHandler(LogLevel.INFO) ); ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(); send(buf, "abc"); send(buf, "www"); channel.writeInbound(buf); } public static void send(ByteBuf buf, String content) { byte[] bytes = content.getBytes(); int length = bytes.length; buf.writeInt(length); // 写入消息长度 buf.writeByte(1); // 写入额外标志位(可选) buf.writeBytes(bytes); // 写入消息内容 } } ``` 在这个例子中,`LengthFieldBasedFrameDecoder` 的参数解释如下: - `1024`:表示最大帧长度。 - `0`:表示长度字段在消息中的起始位置。 - `4`:表示长度字段占用的字节数。 - `1`:表示长度字段之后的偏移量。 - `0`:表示调整值(通常为 0)。 通过这种方式,接收方可以准确地解析出每一条消息[^2]。 #### 2. 使用分隔符解码器 `DelimiterBasedFrameDecoder` 如果每条消息都以特定的分隔符结尾(例如换行符 `\n`),可以使用 `DelimiterBasedFrameDecoder` 来解析消息。以下是示例代码: ```java import io.netty.buffer.Unpooled; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class DelimiterExample { public static void configurePipeline(ChannelPipeline pipeline) { ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes()); // 定义分隔符 pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); // 添加分隔符解码器 pipeline.addLast(new StringDecoder()); // 将 ByteBuf 转换为字符串 pipeline.addLast(new YourBusinessHandler()); // 自定义业务处理器 } } ``` 这种方案的优点是实现简单,但缺点是需要为每条消息附加分隔符,增加了通信开销[^4]。 #### 3. 自定义解码器 对于更复杂的消息格式,可以实现自定义解码器。继承 `ByteToMessageDecoder` 类并重写其 `decode` 方法,根据具体需求解析消息。以下是一个简单的自定义解码器示例: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; public class CustomDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() < 4) { // 检查是否有足够的数据读取长度字段 return; } in.markReaderIndex(); // 标记当前读取位置 int length = in.readInt(); // 读取消息长度 if (in.readableBytes() < length) { // 如果剩余数据不足以组成完整消息 in.resetReaderIndex(); // 回滚读取位置 return; } ByteBuf frame = in.readBytes(length); // 读取消息内容 out.add(frame); // 将解析后的消息添加到输出列表 } } ``` 通过自定义解码器,可以灵活地处理各种复杂的协议[^3]。 --- #### 总结 在 Netty 中,半包问题可以通过以下几种方式解决: - 使用 `LengthFieldBasedFrameDecoder` 解析带有长度字段的消息。 - 使用 `DelimiterBasedFrameDecoder` 解析带有分隔符的消息。 - 实现自定义解码器以适应复杂的协议需求。 每种方法都有其适用场景,选择时需根据实际消息格式和性能要求进行权衡。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值