netty(四)pipline 顺序实践
1. inbound 和 outbound
理论上,InboundHandler顺序执行,OutboundHandler逆序执行,实际上有坑:
//定长解码类
pipeline.addLast(new FixedLengthFrameDecoder(14));
//字符串解码类
pipeline.addLast(new StringDecoder());
//处理类 inbound
pipeline.addLast(new ServerHandler4());
pipeline.addLast(new StringEncoder());
其中,FixedLengthFrameDecoder为,StringDecoder,ServerHandler4为inboundHandler
ServerHandler4接收客户端的字符串,然后返回客户端的字符串
但失败了,表现为服务器能接收,但客户端没有接收到服务器返回的字符串,修改为:
//定长解码类
pipeline.addLast(new FixedLengthFrameDecoder(14));
//字符串解码类
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
//处理类
pipeline.addLast(new ServerHandler4());
这样就可以,分析:
ServerHandler4中的writeAndFlush,会逆序调用OutboundHandler,但不是从pipeline末尾,而是从ServerHandler4往上搜索,结果没找到StringEncoder和其它outBoundHandler,无法完成编码:https://blog.csdn.net/wgyvip/article/details/25637651
还有种方法:ctx.writeAndFlush 改为 ctx.channel().writeAndFlush,channel的write会从pipeline头开始搜索
此外,FixedLengthFrameDecoder需要在StringDecoder之前
2.read函数的传递性
如果不改写ChannelInboundHandlerAdapter的channelRead,则channelRead会调用pipeline中下一个ChannelInboundHandlerAdapter的channelRead:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
所以如果改写了channelRead,则意味着链路其后的ChannelInboundHandlerAdapter不会收到channelRead,除非显式调用fire~:
//获取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//定长解码类
pipeline.addLast(new FixedLengthFrameDecoder(14));
//字符串解码类
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
//处理类
pipeline.addLast(new ServerHandler4());
pipeline.addLast(new ServerHandler5());
class ServerHandler4 extends SimpleChannelInboundHandler<String> {
//用于记录次数
private int count = 0;
//读取客户端发送的数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("RESPONSE--------"+msg+";"+" @ "+ ++count);
/**
* channel的write会从pipeline头上查找outbound
*/
// ctx.channel().writeAndFlush(msg);
// 注意复制
StringBuilder stringBuilder = new StringBuilder().append(msg);
String newMsg = stringBuilder.toString();
ctx.writeAndFlush(newMsg);
// 显式转发
ctx.fireChannelRead("转发:"+ newMsg);
}
最后的fireChannelRead是必须的,覆盖channelRead函数后,消息不再往下传递,需要手动传递
class ServerHandler5 extends SimpleChannelInboundHandler<String> {
//用于记录次数
private int count = 0;
//读取客户端发送的数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("RESPONSE5--------"+msg+";"+" @ "+ ++count);
输出:
RESPONSE--------I am client,--; @ 1
RESPONSE5--------转发:I am client,--; @ 1
RESPONSE--------I am client,--; @ 2
RESPONSE5--------转发:I am client,--; @ 2
RESPONSE--------I am client,--; @ 3
RESPONSE5--------转发:I am client,--; @ 3
RESPONSE--------I am client,--; @ 4
RESPONSE5--------转发:I am client,--; @ 4
RESPONSE--------I am client,--; @ 5
RESPONSE5--------转发:I am client,--; @ 5
注意:
1.因为使用SimpleChannelInboundHandler,转发时要复制msg,或retan byte增加引用寿命
2.如果存在多个handler都要处理msg,可使用ChannelInboundHandlerAdapter,这样可以避免频繁复制,注意手段fire传递,且最后一个adapter要释放msg
8.29
|
1
2
3
4
5
6
7
8
|
class ServerHandler4 extends SimpleChannelInboundHandler<ByteBuf> { // ByteBuf 会跳过此handler //用于记录次数 private int count = 0; //读取客户端发送的数据 @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { System.out.println("RESPONSE--------"+msg+";"+" @ "+ ++count); |
handler4修改为ByteBuf,则会跳过此handler,直接到handler5,输出:

9.18
Handler4中不用复制(红色标注),因为String不是netty对象,不会被回收
浙公网安备 33010602011771号