Netty调用ChannelHandlerContext的writeAndFlush消息未进入Pipeline问题
问题根源
该问题由Netty Pipeline的出站事件传播规则导致,服务端和客户端的问题根源完全一致:
- 你在
PacketDecoder的channelActive方法中保存的是PacketDecoder对应的ChannelHandlerContext实例,该实例绑定了Decoder在Pipeline中的具体节点位置 - Netty中调用任意
ChannelHandlerContext的writeAndFlush方法时,出站事件仅会从当前节点开始,向Pipeline头部方向传播,只会经过当前节点上游的出站处理器 - 你的Pipeline添加顺序是先加
PacketDecoder、后加PacketEncoder,PacketEncoder处于PacketDecoder的下游(更靠近Pipeline尾部),因此从Decoder节点触发的出站事件完全不会走到Encoder节点,自然不会执行编码逻辑,消息也无法正常发出。
解决方案
任选以下一种方案修复即可:
方案1:保存Channel实例而非单个Handler的Context(推荐)
修改channelActive中的赋值逻辑,改为保存整个Channel对象:
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { channel = ctx.channel(); ctx.fireChannelActive(); }
调用Channel.writeAndFlush时,出站事件会从Pipeline最尾部开始传播,会经过所有已注册的出站处理器,就能正常触发PacketEncoder的编码逻辑。
方案2:调整Pipeline的Handler添加顺序
如果一定要使用单个Handler的Context触发写入,可以将所有出站处理器放到入站处理器之前添加:
ch.pipeline().addLast( // 先加Encoder出站处理器 new PacketEncoder(){ @Override protected void encode(ChannelHandlerContext ctx, Pair<PacketRegistry, Packet> msg, ByteBuf out) throws Exception { System.out.println("message encoded on client sent to server"); super.encode(ctx, msg, out); } }, // 再加Decoder入站处理器 new PacketDecoder(clientRegistries){ @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { List<Packet> packets = PacketHelper.handle(in, ctx, super.serverRegistries); packets.forEach(packet -> { System.out.println(packet.getClass()); }); // 补充:如果需要后续入站处理器接收数据包,需要将解析结果添加到out列表 // out.addAll(packets); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { channel = ctx; ctx.fireChannelActive(); } } );
该方式下PacketEncoder处于PacketDecoder的上游,从Decoder节点触发的出站事件就能经过Encoder完成编码,但不符合常规的Pipeline编排习惯,更推荐使用方案1。
额外注意
你当前重写的decode方法仅打印了解析到的Packet,没有将Packet添加到out输出列表,会导致后续的入站业务处理器无法收到解析后的数据包,如有入站处理需求请补充对应逻辑。
内容的提问来源于stack exchange,提问作者Hrishikesh Ingle
相关产品推荐
相关产品推荐

