使用TCP KeepAlive检测套接字断开连接

10

我正在开发一个通过TCP/IP托管第三方设备的服务器,但是我一直遇到突然的连接断开问题(这些设备通过蜂窝网络连接)。我需要找到一种无需向设备本身写入数据即可检测到断开的方法。

我已经尝试使用TCP keepalive功能,但Java似乎不允许调整keepalive操作的时间。

是否有任何建议的方法可以解决这个问题?

以下是我的简化套接字代码:

public class Test2Socket {
    public static void main(String[] args) {
        try {
            ServerSocket skt = new ServerSocket(1111);

            Socket clientSocket = skt.accept();

            clientSocket.setKeepAlive(true);

            System.out.println("Connected..");

            BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            String inputLine;

            while((inputLine = input.readLine()) != null)
            {
                System.out.println(inputLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
任何反馈将不胜感激。

为什么无法向客户端写入数据?保持连接间隔通常固定为2小时,因此不适用于快速检测断开的连接。 - jarnbjo
我尝试过了。设备只是向我抛出了一个错误,并且有点发脾气了。 - PeteMitchell
请查看我的案例和解决方案 https://dev59.com/y3VD5IYBdhLWcg3wKoWH#31741436 - Davut Gürbüz
出奇地,错误的答案成为了投票最多并被接受的解决方案。对我来说,检测套接字超时/网络断开是TCP存在的主要原因。这个方面不应该在应用程序级别处理,应该发送NOPs。TCP已经发送SYN/ACK数据包。 - user9016329
5个回答

9
您不能仅依靠TCP堆栈内置的保活机制来实现长时间连接。这是因为保活间隔无法由您的应用程序调整,而是由操作系统设置的,默认值相当高(几个小时)。这不仅限于Java。
如果您需要在合理的时间内超时,则必须在要使用的协议中实现某种保活功能。我见过的大多数高级协议都具有一些NOP功能,其中您发送一个“你在那里吗?”消息,另一方回复一个“是的,我在这里”的消息,而不做其他任何事情。

1
你问题的第一部分是不正确的。当然有系统调用可设置定时器:setsockopt(...TCP_KEEPIDLE...)。 - Andreas Florath
1
第二部分的建议是危险的,会给客户端和服务器增加复杂性:您在这里混淆了第4层和第7层(必须手动解决所有问题)。TCP可以提供您想要的功能-为什么要再做一遍呢?有技术解决方案-即使对于Java也是如此。 - Andreas Florath
2
@AndreasFlorath:我认为原帖作者真的很想看到:a)关于Windows、Mac OS X等系统调用的文档链接;b)如何以可移植的方式从Java中使用它们。 - Laszlo Valko
1
@AndreasFlorath 在所有平台上,以及Java中并不存在为每个套接字设置保持活动定时器的套接字选项。 - user207421
@R.G.:您没有抓住重点。SO_KEEPALIVE 不够好。 - Laszlo Valko
显示剩余2条评论

4

如果要深入讨论TCP Keep-Alives,可以查看我的回答这里

但基本上,TCP Keep-Alives可能是检测到过期连接的最佳方法。主要问题是,操作系统默认设置为在连接检查之前等待2小时,并发送11分钟的Keep-Alive数据包,然后才会实际断开连接。

当TCP已经内置了应用层Keep Alive协议时,请不要编写自己的协议。您只需要将TCP超时时间设置为2-3分钟之类更合理的数值即可。

不幸的是,由于TCP超时时间是在操作系统级别而不是JVM内部管理的,因此很难(但不是不可能)在代码中按每个套接字配置TCP超时时间。


0

设置一个读取超时时间,使用setSoTimeout()方法,设定一个合理的值,比预期的响应时间多一倍,并捕获可能出现的SocketTimeoutException

注意,"Java TCP KeepAlive"并不存在。


0
当您在套接字上调用setKeepalive()时,系统参数(可调整)将被使用。(在Debian 8下使用openjdk7进行了检查。)
因为我需要完全相同的功能,所以我编写了一个名为libdontdie的小型库,可以预加载并与Java一起使用。

0

this库的作者在这里 :)

正如之前所述,除非您依赖于在TCP协议中直接实现的难以配置的功能(保持连接),否则您几乎无法在不通过连接发送数据的情况下检测到故障。

引用的库封装了传统的Java Sockets,同时添加了易于配置的定期连接检查和ACK。 (这些对应用程序员来说都是不可见的)。 一旦检测到故障,所有注册的观察者都将收到通知。 (根据您的配置,这可能接近实时)。

优点是该库相当易于使用(并且至少在我的项目中非常可靠)。

缺点是,应用程序层不适合用于连接检查。 因此,基本上该库正在以一种它们可能不应该使用的方式使用它们。

附带说明: 您提到您的客户端是移动设备。 虽然链接库在Android上运行,但您可以查看Push Notification Services而不是自己处理连接问题。 这可以改善例如电池消耗。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接