【发布时间】:2018-03-18 18:07:15
【问题描述】:
我在 java 应用程序中使用 netty.io (4.0.4) 来实现 TCP 客户端以与外部硬件驱动程序通信。该硬件的要求之一是,客户端每 30 秒发送一次 KEEP_ALIVE(心跳)消息,但是硬件不响应此心跳。 我的问题是,当连接突然断开时(例如:网线被拔出),客户端完全没有意识到这一点,并在收到操作超时异常之前继续发送 KEEP_ALIVE 消息更长时间(大约 5-10 分钟)。 换句话说,从客户端,没有办法判断它是否仍然连接。
如果有帮助,下面是我的引导设置的 sn-p
// 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,这种行为是有道理的,但我仍然希望有一些方法可以确定写入是否“真正”成功。(让硬件确认心跳是不可能的)
【问题讨论】:
-
或许看看这里的答案? stackoverflow.com/questions/21358800/…
-
感谢该链接,我在提出问题之前查看了该链接,我对该解决方案的问题是:由于网线已拔下,无法正常关闭通道 b.实现 ReadTimeoutHandler 将不起作用,因为硬件并没有说太多,所以这会经常被触发:/(我在问题中谈论的 ack 是 TCP 层 ack 而不是应用程序级别)。有道理?也许我想要的 TCP 甚至都无法实现,这就是问题的一部分。
-
我希望您在几分钟后收到“连接重置”或“软件导致连接中止”。您确定在发送心跳时正确检测到发送错误吗?
-
@EJP 也许我没有正确检测到错误,我所做的只是像这样发送心跳.. IdleStateEvent e = (IdleStateEvent) evt;频道频道 = ctx.channel(); if (e.state() == IdleState.ALL_IDLE) { LOGGER.info("发送 KEEP_ALIVE_MESSAGE");通道.writeAndFlush(KEEP_ALIVE_MESSAGE); }