|
@@ -1,8 +1,5 @@
|
|
|
package com.qlm.netty;
|
|
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 com.qlm.netty.handler.NettyServerHandler;
|
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
|
import io.netty.channel.ChannelFuture;
|
|
import io.netty.channel.ChannelFuture;
|
|
@@ -12,7 +9,12 @@ import io.netty.channel.EventLoopGroup;
|
|
|
import io.netty.channel.nio.NioEventLoopGroup;
|
|
import io.netty.channel.nio.NioEventLoopGroup;
|
|
|
import io.netty.channel.socket.SocketChannel;
|
|
import io.netty.channel.socket.SocketChannel;
|
|
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
|
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.handler.timeout.IdleStateHandler;
|
|
|
|
|
+import io.netty.util.CharsetUtil;
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
@@ -43,12 +45,32 @@ public class NettyServer {
|
|
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
|
|
@Override
|
|
@Override
|
|
|
protected void initChannel(SocketChannel ch) {
|
|
protected void initChannel(SocketChannel ch) {
|
|
|
- // 添加空闲状态处理器(读超时时间设为30秒,每30秒检查一次心跳状态)
|
|
|
|
|
|
|
+ // 空闲状态处理器
|
|
|
ch.pipeline().addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
|
|
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());
|
|
ch.pipeline().addLast(new NettyServerHandler());
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|