Netty:channel.write在断开连接时挂起

时间:2013-08-30 10:40:36

标签: java exception-handling netty disconnect

我有以下情况:

以这种方式打开新的频道连接:

    ClientBootstrap bootstrap = new ClientBootstrap(
             new OioClientSocketChannelFactory(Executors.newCachedThreadPool()));

    icapClientChannelPipeline = new ICAPClientChannelPipeline();           
    bootstrap.setPipelineFactory(icapClientChannelPipeline);
    ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
    channel = future.awaitUninterruptibly().getChannel();

这是按预期工作的。

以下列方式将信息写入频道:

channel.write(chunk)

当与服务器的连接仍然存在时,这也可以正常工作。但是,如果服务器出现故障(计算机脱机),则呼叫会挂起并且不会返回。

我通过在channel.write(chunk)之前和之后添加日志语句来确认这一点。连接断开时,仅显示之前的日志语句。

  1. 造成这种情况的原因是什么?我以为这些调用都是异步并立即返回?我也试过NioClientSocketChannelFactory,行为相同。

  2. 我尝试使用channel.getCloseFuture()但是听众永远不会被调用,我在使用channel.isOpen()channel.isConnected()channel.isWritable()写入之前尝试检查频道总是如此......

  3. 如何解决这个问题?没有异常被抛出,也没有真正发生......像this onethis one这样的问题表明,如果没有心跳,就无法检测到通道断开。但我无法实现心跳,因为我无法改变服务器端。

  4. 环境:Netty 3,JDK 1.7

1 个答案:

答案 0 :(得分:0)

好的,我上周解决了这个问题,所以我会为完整性添加答案。

我错了3.因为我认为我必须改变客户端和服务器端以获得心跳。如上所述in this question,您可以使用IdleStateAwareHandler来实现此目的。我这样实现了它:

IdleStateAwareHandler:

public class IdleStateAwareHandler extends IdleStateAwareChannelHandler {

    @Override
    public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
        if (e.getState() == IdleState.READER_IDLE) {
            e.getChannel().write("heartbeat-reader_idle");
        }
        else if (e.getState() == IdleState.WRITER_IDLE) {
            Logger.getLogger(IdleStateAwareHandler.class.getName()).log(
                    Level.WARNING, "WriteIdle detected, closing channel");
            e.getChannel().close();
            e.getChannel().write("heartbeat-writer_idle");
        }
        else if (e.getState() == IdleState.ALL_IDLE) {
            e.getChannel().write("heartbeat-all_idle");
        }
    }
}

PipeLine:

public class ICAPClientChannelPipeline implements ICAPClientPipeline {

        ICAPClientHandler icapClientHandler;
        ChannelPipeline pipeline;

        public ICAPClientChannelPipeline(){
            icapClientHandler = new ICAPClientHandler();
        pipeline = pipeline(); 
            pipeline.addLast("idleStateHandler", new IdleStateHandler(new HashedWheelTimer(10, TimeUnit.MILLISECONDS), 5, 5, 5));
            pipeline.addLast("idleStateAwareHandler", new IdleStateAwareHandler());
            pipeline.addLast("encoder",new IcapRequestEncoder());
            pipeline.addLast("chunkSeparator",new IcapChunkSeparator(1024*4));
            pipeline.addLast("decoder",new IcapResponseDecoder());
            pipeline.addLast("chunkAggregator",new IcapChunkAggregator(1024*4));
            pipeline.addLast("handler", icapClientHandler);            
        }

        @Override
    public ChannelPipeline getPipeline() throws Exception {
            return pipeline;
    }                     
}

5秒后检测通道上的任何读或写空闲状态。 正如您所看到的那样,它有点特定于ICAP,但这与问题无关。

要对空闲事件做出反应,我需要以下监听器:

channel.getCloseFuture().addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
            doSomething();
    }
});