瀏覽代碼

feat:解决Netty消息接收拆包的问题

yingjian.wu 2 月之前
父節點
當前提交
cc60d1bd38
共有 1 個文件被更改,包括 30 次插入8 次删除
  1. 30 8
      src/main/java/com/qlm/netty/NettyServer.java

+ 30 - 8
src/main/java/com/qlm/netty/NettyServer.java

@@ -1,8 +1,5 @@
 package com.qlm.netty;
 
-import com.jfinal.kit.PropKit;
-import com.qlm.netty.codec.StringDecoder;
-import com.qlm.netty.codec.StringEncoder;
 import com.qlm.netty.handler.NettyServerHandler;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.ChannelFuture;
@@ -12,7 +9,12 @@ 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.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
 import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.CharsetUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,12 +45,32 @@ public class NettyServer {
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) {
-                            // 添加空闲状态处理器(读超时时间设为30秒,每30秒检查一次心跳状态)
+                            // 空闲状态处理器
                             ch.pipeline().addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
-                            // 添加自定义编解码器
-                            ch.pipeline().addLast(new StringDecoder());
-                            ch.pipeline().addLast(new StringEncoder());
-                            // 添加业务处理器
+                            // --- ChannelPipeline 顺序非常重要 ---
+                            // 【入站 Inbound - 负责解码】
+                            // 1. 【核心解码】LengthFieldBasedFrameDecoder (基于长度字段的帧解码器)
+                            //    maxFrameLength: 8192 (单条消息最大长度)
+                            //    lengthFieldOffset: 0 (长度字段的偏移量,即长度头从第0个字节开始)
+                            //    lengthFieldLength: 4 (长度字段占4个字节)
+                            //    lengthAdjustment: 0 (长度调整值,数据包长度=长度头的值+adjustment)
+                            //    initialBytesToStrip: 4 (解码后,跳过前面4个字节的长度头,只保留数据体)
+                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
+                            // 2. 【解码】StringDecoder (字符串解码器)
+                            //    将 LengthFieldBasedFrameDecoder 传来的、完整的 ByteBuf 数据体按 UTF-8 解码成字符串
+                            ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
+                            // 【出站 Outbound - 负责编码】(注意:出站是反向执行的)
+                            // 1. 【核心编码】LengthFieldPrepender (长度字段前置器)
+                            //    指定用 4 个字节来存放长度。
+                            //    当您 write(String) 时,它会等 StringEncoder 把 String 转成 ByteBuf (数据体)后,
+                            //    自动计算这个 ByteBuf 的长度,并把长度(4字节)加到这个 ByteBuf 的最前面。
+                            ch.pipeline().addLast(new LengthFieldPrepender(4));
+                            // 2. 【编码】StringEncoder (字符串编码器)
+                            //    将您在 Handler 中 write 的 String 按 UTF-8 编码成 ByteBuf (数据体)
+                            ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
+                            // 【入站】
+                            // 3. 您的业务 Handler
+                            //    它收到的 msg 就是 StringDecoder 解码后的完整 JSON 字符串
                             ch.pipeline().addLast(new NettyServerHandler());
                         }
                     });