nzomkxia
nzomkxia

Reputation: 1267

what's the sequence of channelhandler in netty?

I have an application using netty to implement both server and client end. Server end sends the current time to client end.

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("in timeserverhandler");
        ChannelFuture f = ctx.writeAndFlush(new UnixTime());
        f.addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

the encoder:

  public class TimeEncoder extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("in timeencoder");
        UnixTime m = (UnixTime) msg;
        ByteBuf encoded = ctx.alloc().buffer(4);
        encoded.writeInt(m.value());
        ctx.write(encoded, promise); // (1)
    }


}

public class TimeServer { private static final int PORT = 9000;

public static void main(String[] args) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        ChannelPipeline p = ch.pipeline();
                        p.addLast(new TimeEncoder(), new TimeServerHandler());
                       //p.addLast(new TimeServerHandler(), new TimeEncoder());

                    }
                });
        ChannelFuture f = b.bind(PORT).sync();
        f.channel().closeFuture().sync();
    } finally {
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

}

}

In TimeServer, if I change the addList sequnce to the commented line, the Encoder handler will never be called and client side can not print out the current time. Why is that, and what's the excute sequence of handlers in the pipeline?

Upvotes: 4

Views: 3720

Answers (4)

Manish Bansal
Manish Bansal

Reputation: 2671

Short answer: The reason for your commented line to not work is that netty does not understand the java object (UnixTime()). It only understands binary data (ByteBuf).

Explanation: Now the sequence of handler execution depends on the sequence you add them in the pipeline. For inbound data, handlers are executed from first to last. And for outbound data, handlers are executed from last to first. Now during handler execution, netty checks if your handler is capable for handling inbound/Outbound data. This is done by checking if your handler extends ChannelInboundHandlerAdapter or/and ChannelOutboundHandlerAdapter. If it extends ChannelInboundHandlerAdapter, then the same will be executed for inbound data. Or if extends ChannelOutboundHandlerAdapter, it will be executed for outbound data.

Now, in working code, your first handler is encoder(handling outbound event) and the second one is writing java object(handling inbound event). In this case, whenever channel becomes active, inbound event will be generated and same will be passed to first handler in pipeline which is your last handler. Now, that handler will consume the event and write data on the channel which is an outbound event. Now, this outbound even will propagate upstream and goes to the next handler in pipeline which handles outbound event which is your encoder. Now, encoder will convert the unixtime into binary data and same will be passed onto the channel.

Now, in non-working code, when you reverse the order of handler, once the channel becomes active, inbound event will be passed to your first outbound event handler which is the last handler in pipeline (First from beginning but last from the end ). Once, this handler generates unix time, it will generate outbound event which will further propagate upstream. But after this, there is no upstream event handler to consume outbound event and hence your unix time will never be converted into binary data and hence this does not work.

Hope it clarifies.

Upvotes: 1

johnstlr
johnstlr

Reputation: 1431

It's because you're using ChannelHandlerContext.write as opposed to Channel.write. http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html states that ChannelHandlerContext methods forward events to the next handler in the pipeline (either upstream or downstream depending on the event type), not from the start of the pipeline. In your code the pipeline is

  • Network
  • TimeEncoder
  • TimeServerHandler

which is fine, but the commented code is

  • Network
  • TimeServerHandler
  • TimeEncoder

which attempts to write back to the network directly as TimeEncoder is upstream of TimeServerHandler.

Upvotes: 0

When creating the pipeline I allways put "addLast(decoder, encoder, handler)".

Check this out: http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html in "Building a pipeline" section.

Upvotes: 0

Frederic Br&#233;gier
Frederic Br&#233;gier

Reputation: 2206

Pedro is right.

You may generally insert first decoders, then encoders, then finally your application handler.

In general the logic is: decoder followed by encoder

If you have a multiple codec logic (say for instance first codec must be followed by a second codec with a handler in between), then the logic will be:

  • pipeline.addLast(decoderProtocol1, encoderProtocol1) followed eventually by .addLast(intermediaryHandler1)
  • pipeline.addLast(decoderProtocol2, encoderProtocol2) followed eventually by .addLast(intermediaryHandler2)
  • ...
  • pipeline.addLast(decoderProtocoln, encoderProtocoln)
  • pipeline.addLast(finalHandler)

Some decoder/encoder come also with one handler, as codec, then obviously you just replace pipeline.addLast(decoderProtocoln, encoderProtocoln) by pipeline.addLast(codecProtocoln).

The correct link to the documentation is: http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html

Upvotes: 2

Related Questions