3
votes

I am using Netty 4.1.0.Final and I am facing an issue where message is not passing through outbound handler. I am posting a sample program where there is one inbound and one outbound handler. Inbound handler uses writeAndFlush in the ChannelHandlerContext and my understanding is that it should forward the message to the first available outbound handler available in the pipeline. Memory Management is ignored for simplicity.

Bootstrap Code

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .localAddress(12021)
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 protected void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new TestHandler1(), new TestOutHandler1());
                 }
             });
ChannelFuture future = bootstrap.bind().sync();
System.out.println("Server Started...");
    future.channel().closeFuture().sync();
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    System.out.println("Server Shutdown");

Inbound Handler Code

public class TestHandler1 extends ChannelInboundHandlerAdapter {


private static Log logger = LogFactory.getLog(TestHandler1.class);

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    logger.info("channelRead");
    ctx.writeAndFlush(msg);
}

}

Outbound Handler Code

public class TestOutHandler1 extends ChannelOutboundHandlerAdapter {

private static Log logger = LogFactory.getLog(TestOutHandler1.class);

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    logger.info("write");
    ctx.writeAndFlush(msg);
}

}

Output

INFO TestHandler1: channelRead

If I change my Inbound Handler as below by doing writeAndFlush() on channel instead of ChannelHandlerContext, I am getting the expected output

Modified Inbound Hanndler

public class TestHandler1 extends ChannelInboundHandlerAdapter {

private static Log logger = LogFactory.getLog(TestHandler1.class);

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    logger.info("channelRead");
    ctx.channel().writeAndFlush(msg);
}

}

Output

INFO TestHandler1: channelRead

INFO TestOutHandler1:write

As per Norman's explanation in the below link, I understand as ChannelHandlerContext.write(...) should pass through ChannelOutboundHandlers that are in front of it, which in my case is the only outbound handler.

Norman's Explanation

Let me know if my understanding is wrong or am I missing anything.

3

3 Answers

5
votes

Previous answers are great, I'd like to explain another aspect of the question: different results got by calling ctx.writeAndFlush() and ctx.channel.writeAndFlush()

Structure of your Channel

  1. ctx.writeAndFlush()
    Find the first OutboundHandler before current ctx. In your channel, nothing is found. So only one info is printed.
  2. ctx.channel.writeAndFlush()
    Actually, ctx.channel.writeAndFlush() will call pipeline.tail.writeAndFlush(). This equals to call writeAndFlush() on tail ctx in your channel. In this way, TestOutHandler1 will be found and TestOutHandler1.write() will be called. Then two infos are printed.
3
votes

Its because you add the ChannelOutboundHandler before the ChannelInboundHandler to the ChannelPipeline. ChannelHandlerContext.writeAndFlush(...) will start on the point in the ChannelPipeline where the ChannelHandler is added. So add the ChannelOutboundHandler before it will work as expected.

0
votes

According to the source code, whatever the the type of the handler is added(inbound or outbound),each handler is chained in one pipeline.Each time if a handler invoke the write method of the according ChannelHandlerContext, the message will be passed to the just previous nearest handler of OutboundHandler type.Just refer to the source code below:

private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

the message will only be sent to the OutboundHandler just before itself.So if you want to pass message to the OutboundHandler,you should first add your OutboundHandler, make sure that the OutboundHandler is just added before the InboundHandler, then the OutboundHandler will get the message.