I'm trying to get into Netty using a simple server-client application (code see below).
I'm struggling with two issues:
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.
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).
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();
}
}
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 wayctx.fireChannelRead
will send the message to the next following handler (opposite way), so Inbound waySee 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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With