我有一个使用netty来实现服务器和客户端的应用程序。服务器端将当前时间发送到客户端。
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();
}
}
编码器:
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)
}
}
公共类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(); } }
}
在TimeServer中,如果我将addList
sequnce更改为注释行,则永远不会调用编码器处理程序,并且客户端无法打印出当前时间。为什么这样,以及管道中处理程序的执行顺序是什么?
答案 0 :(得分:2)
佩德罗是对的。
您通常可以插入第一个解码器,然后插入编码器,最后插入应用程序处理程序。
通常逻辑是:解码器后跟编码器
如果您有多个编解码器逻辑(例如,第一个编解码器后面必须跟第二个编解码器,中间有一个处理程序),那么逻辑将是:
pipeline.addLast(decoderProtocol1, encoderProtocol1)
最后是.addLast(intermediaryHandler1)
pipeline.addLast(decoderProtocol2, encoderProtocol2)
最后是.addLast(intermediaryHandler2)
pipeline.addLast(decoderProtocoln, encoderProtocoln)
pipeline.addLast(finalHandler)
某些解码器/编码器也带有一个处理程序,作为编解码器,显然您只需将pipeline.addLast(decoderProtocoln, encoderProtocoln)
替换为pipeline.addLast(codecProtocoln)
。
文档的正确链接是: http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
答案 1 :(得分:0)
在创建管道时,我总是把&#34; addLast(解码器,编码器,处理程序)&#34;。
检查一下:{&3 34}&#34;建立管道&#34;部分。
答案 2 :(得分:0)
这是因为你使用ChannelHandlerContext.write而不是Channel.write。 http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html声明ChannelHandlerContext方法将事件转发到管道中的下一个处理程序(上游或下游,具体取决于事件类型),而不是从管道的开始。在您的代码中,管道是
这很好,但注释的代码是
尝试直接写回网络,因为TimeEncoder是TimeServerHandler的上游。
答案 3 :(得分:0)
简短回答:您的注释行不起作用的原因是netty不了解java对象(UnixTime())。它只能理解二进制数据(ByteBuf)。
说明:现在,处理程序执行的顺序取决于您在管道中添加它们的顺序。对于入站数据,处理程序从头到尾执行。对于出站数据,处理程序从最后到第一个执行。现在,在处理程序执行期间,netty会检查您的处理程序是否能够处理入站/出站数据。这是通过检查您的处理程序是否扩展ChannelInboundHandlerAdapter或/和ChannelOutboundHandlerAdapter来完成的。如果它扩展了ChannelInboundHandlerAdapter,则对入站数据执行相同的操作。或者,如果扩展ChannelOutboundHandlerAdapter,它将针对出站数据执行。
现在,在工作代码中,您的第一个处理程序是编码器(处理出站事件),第二个处理程序是编写java对象(处理入站事件)。在这种情况下,每当通道变为活动状态时,将生成入站事件,并将相同的内容传递给管道中的第一个处理程序,这是您的最后一个处理程序。现在,该处理程序将使用事件并在作为出站事件的通道上写入数据。现在,这个出站甚至将传播到上游并转到管道中的下一个处理程序,该处理程序处理作为编码器的出站事件。现在,编码器将unixtime转换为二进制数据,同样将传递到通道。
现在,在非工作代码中,当您反转处理程序的顺序时,一旦通道变为活动状态,入站事件将被传递给您的第一个出站事件处理程序,这是管道中的最后一个处理程序(从开始但是从最后一个开始结束 )。一旦此处理程序生成unix时间,它将生成将进一步向上游传播的出站事件。但在此之后,没有上游事件处理程序来消耗出站事件,因此你的unix时间永远不会被转换为二进制数据,因此这不起作用。
希望它澄清。