title: netty client takes very long before broken network is detected
body: <p>I am using netty.io (4.0.4) in a java application to implement a TCP client to communicate with an external hardware driver. One of the requirements of this hardware is, the client send a KEEP_ALIVE (heart-beat) message every 30 seconds, the hardware however does not respond to this heat-beat. 
  My problem is, when the connection is abruptly broken (eg: network cable unplugged) the client is completely unaware of this, and keeps sending the KEEP_ALIVE message for much longer (around 5-10 minutes) before it gets an operation timeout exception. 
  In other words, from the client side, there is no way to tell if its still connected. </p>
  
  <p>Below is a snippet of my bootstrap setup if it helps</p>
  
  <pre><code>// bootstrap setup
  bootstrap = new Bootstrap().group(group)
              .channel(NioSocketChannel.class)
              .option(ChannelOption.SO_KEEPALIVE, true)
              .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
              .remoteAddress(ip, port)
              .handler(tcpChannelInitializer);
  
  
  // part of the pipeline responsible for keep alive messages
      pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS));
      pipeline.addLast("keepAliveHandler", keepAliveMessageHandler);
  </code></pre>
  
  <p>I would expect since the client is sending keep alive messages, and those messages are not received at the other end, a missing acknowledgement should indicate a problem in the connection much earlier? </p>
  
  <p><strong>EDIT</strong></p>
  
  <p>Code from the KeepAliveMessageHandler</p>
  
  <pre><code>public class KeepAliveMessageHandler extends ChannelDuplexHandler
  {
  
      private static final Logger LOGGER = getLogger(KeepAliveMessageHandler.class);
  
      private static final String KEEP_ALIVE_MESSAGE = "";
  
  
      @Override
      public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception
      {
          if (!(evt instanceof IdleStateEvent)) {
              return;
          }
  
          IdleStateEvent e = (IdleStateEvent) evt;
          Channel channel = ctx.channel();
  
          if (e.state() == IdleState.ALL_IDLE) {
              LOGGER.info("Sending KEEP_ALIVE_MESSAGE");
              channel.writeAndFlush(KEEP_ALIVE_MESSAGE);
          }
      }
  }
  </code></pre>
  
  <p><strong>EDIT 2</strong> </p>
  
  <p>I tired to explicitly ensure the keep alive message delivered using the code below </p>
  
  <pre><code>@Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception
      {
          if (!(evt instanceof IdleStateEvent)) {
              return;
          }
  
          IdleStateEvent e = (IdleStateEvent) evt;
          Channel channel = ctx.channel();
  
          if (e.state() == IdleState.ALL_IDLE) {
              LOGGER.info("Sending KEEP_ALIVE_MESSAGE");
              channel.writeAndFlush(KEEP_ALIVE_MESSAGE).addListener(future -&gt; {
  
                  if (!future.isSuccess()) {
                      LOGGER.error("KEEP_ALIVE message write error");
                      channel.close();
                  }
              });
          }
      }
  </code></pre>
  
  <p>This also does not work. :( according to <a href="https://stackoverflow.com/a/18583412/3580481">this answer</a> this behavior makes sense, but I am still hoping there is some way to figure-out if the write was a "real" success. (Having the hardware ack the hear-beat is not possible)</p>