2017-10-06 110 views
0

我在java应用程序中使用netty.io(4.0.4)来实现与外部硬件驱动程序进行通信的TCP客户端。这种硬件的一个要求是,客户端每隔30秒发送一次KEEP_ALIVE(心跳)消息,但硬件不响应这种热击。 我的问题是,当连接突然中断(例如:拔掉网络电缆)时,客户端完全不知道这一点,并在获取操作超时异常之前持续发送KEEP_ALIVE消息更长时间(大约5-10分钟)。 换句话说,从客户端来说,无法判断它是否仍然连接。netty客户端需要很长时间才能检测到网络故障

下面是我的引导设置的片段,如果它有助于

// 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); 

,因为客户端发送保持活动的消息我会想到,这些消息不会在另一端接收,丢失的确认应指示连接问题早得多?

编辑

从KeepAliveMessageHandler

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); 
     } 
    } 
} 

编辑代码2

我累了明确保证保持活动消息使用的代码交付以下

@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 -> { 

       if (!future.isSuccess()) { 
        LOGGER.error("KEEP_ALIVE message write error"); 
        channel.close(); 
       } 
      }); 
     } 
    } 

这也行不通。 :(根据this answer这种行为是有道理的,但我仍然希望有一些方法可以计算出写入是否是“真实”的成功。(硬件确认听不到节拍是不可能的)

+1

也许看看答案在这里? https://stackoverflow.com/questions/21358800/tcp-keep-alive-to-determine-if-client-disconnected-in-netty –

+0

感谢您的链接,我看了之前,我问这个问题,问题我有与该解决方案是: a。由于网线已拔出,因此不能正常关闭通道 b。实施ReadTimeoutHandler将无法正常工作,因为硬件不会说太多,所以这会经常触发:/(我在谈论的问题是TCP层ack不是应用程序级)。合理?也许我想要的甚至不可能通过TCP,这是问题的一部分。 – codeCruncher

+0

我希望您在几分钟后得到'连接重置'或'软件引起的连接中止'。您确定在发送听力片时是否正确地检测到发送错误? – EJP

回答

0

您已启用TCP保持连接

.option(ChannelOption.SO_KEEPALIVE, true) 

但在你的代码,我看不到,确保存活为30秒的速度发送任何一块。

如果连接已经终止导致的TCP Keepalive超时,另一台主机最终为旧连接发送一个数据包终止连接的主机将发送一个RST标志设置为的信息包,以通知另一个主机旧连接不再处于活动状态。这将强制另一个主机终止连接的结束,以便建立新的连接。

通常TCP Keepalive每隔45或60秒发送一次空闲的TCP连接,连接在丢失3个sequental ACK后丢弃。这由主机而异,例如,默认情况下,Windows PC在7200000ms(2小时)后发送第一个TCP Keepalive数据包,然后以1000ms的间隔发送5个Keepalive,如果没有响应任何Keepalive数据包,则丢弃连接。

(采取形式http://ltxfaq.custhelp.com/app/answers/detail/a_id/1512/~/tcp-keepalives-explained_

我现在明白了,

pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS)); 
pipeline.addLast("keepAliveHandler", keepAliveMessageHandler); 

将触发一个空闲事件相互活动和keepAliveMessageHandler每隔30秒就会发送一个数据包以除去副在这种情况下。

不幸的是

ChannelFuture future = channel.writeAndFlush(KEEP_ALIVE_MESSAGE); 

在写入OS缓冲区时被认为是成功的。

看来你的条件下,你只有2 optios:

  1. 发送一个命令,将有来自外部 设备的一些响应(的东西,不会造成distruption)
    但我会假设,这在你的情况下是不可能的。

  2. 实现底层TCP驱动程序设置
    TCP keepalive的默认操作系统设置更多地关于节省系统资源以支持大量的应用程序和连接。假如你有一个专门的系统,你可以设置更积极的TCP检查配置。 以下是关于如何对Linux内核进行调整的链接:http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
    该解决方案应该在普通安装以及VM和Docker容器中工作。

专题概述信息:https://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

+0

我确实看过SO帖子,就像我在后续评论中所说的那样,解决方案不会工作(我试过了,如果没有阅读,它会引发异常,这并不一定意味着连接已经死了,这不是我想要的)。此外,我已将KeepAliveHandler的代码添加到原始问题中。我很感激你的努力来帮助 – codeCruncher

+0

现在它是一个不同的故事。我有一个更新给你。如果它没有帮助,请添加关于您的发送超时,重试次数以及您的KEEP_ALIVE_MESSAGE究竟是什么的信息。 –

+0

所以,我没有尝试处理是在writeAndFlush()调用后返回的ChannelFuture,像这样: channel.writeAndFlush(KEEP_ALIVE_MESSAGE).addListener(未来 - > { 如果(future.isSuccess()){ 记录仪.error(“KEEP_ALIVE message write error”); channel.close(); } }); 但这不起作用,如果块没有执行!我读netty说,当数据写入IO缓冲区时成功,而不是在另一端收到数据。 – codeCruncher

相关问题