Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Netty Handler not called

I'm trying to get into Netty using a simple server-client application (code see below).

I'm struggling with two issues:

  1. the ConfigServerHandler resp. ConfigClientHandler is called correctly. But the FeedbackServerHandler resp. FeedbackClientHandler is never called. Why? According to the documentation the Handlers should be called one after another.

  2. I'd like to have several handlers. Each of these handlers is interested in only some of the messages that are send by the other side (e.g. send by client, received by server).

    • should I filter the messages after they have been received by a handler (channelRead)? How can I distinguish different Strings? With different objects this should be pretty easy by parsing them.
    • is it possible to define different ChannelPipelines for a SocketChannel?
    • further approaches?

Thanks for your help!

KJ

This is how the server is created:

public void run() 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) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 p.addLast(
                     new ObjectEncoder(),
                     new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                     new ConfigServerHandler(),
                     new FeedbackServerHandler());
             }
         });
      b.bind(mPort).sync().channel().closeFuture().sync();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

One of the Handler classes (the FeedbackServerHandler does exactly the same but parses into Integer):

public class ConfigServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("ConfigServerHandler::channelRead, " +(String)msg);
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

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

The client side looks pretty similar:

public Client(String host, int port) throws InterruptedException {

    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(workerGroup)
         .channel(NioSocketChannel.class)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                  p.addLast(
                      new ObjectEncoder(),
                      new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                      new ConfigClientHandler(),
                      new FeedbackClientHandler());
             }
         });
         b.connect(host, port).sync().channel().closeFuture().sync();
    } finally {
        workerGroup.shutdownGracefully();
    }
}

And here is one of the Client side handlers (the other one sends an Integer message and parses into Integer in the 'channelRead' method):

public class ConfigClientHandler extends ChannelInboundHandlerAdapter {

    private final String firstMessage = "blubber";

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("ConfigClientHandler::channelActive");
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("ConfigClientHandler::channelRead, " +(String)msg);
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

}

like image 230
KayJ Avatar asked Nov 21 '14 07:11

KayJ


1 Answers

You're using ChannelInboundHandlerAdapter, which is fine, for your "middle" handler ConfigXxxxxHandler.

But you use channelRead method and then use inside ctx.write(msg). ctx.write(msg) will write the msg back to the other server through the previous handler first (ObjectDecoder), not to the next handler (FeedbackClientHandler in your case).

You should use the following if you want to send the message to the next handler:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    System.out.println("ConfigClientHandler::channelRead, " +(String)msg);
    ctx.fireChannelRead(msg);
}

And of course no ctx.flush() in channelReadComplete (since no more write there). But in your final FeedbackClientHandler, of course, use the flush method with ctx.write(yourNewMessage) or use ctx.writeAndFlush(yourNewMessage).

So to resume:

  • ctx.write will send the message to the wire, so to the previous handler down to the channel then to the network, so Outbound way
  • ctx.fireChannelRead will send the message to the next following handler (opposite way), so Inbound way

See http://netty.io/wiki/new-and-noteworthy-in-4.0.html#wiki-h4-16 for details.

You should perhaps also invert Encoder/Decoder, since in general it is good idea to have first the decoder, then the encoder in the pipeline.

            p.addLast(
                      new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                      new ObjectEncoder(),
                      new ConfigClientHandler(),
                      new FeedbackClientHandler());
like image 198
Frederic Brégier Avatar answered Sep 28 '22 08:09

Frederic Brégier