如何确定客户端连接是不是在 Java 服务器的 TCP 套接字连接中终止?

Posted

技术标签:

【中文标题】如何确定客户端连接是不是在 Java 服务器的 TCP 套接字连接中终止?【英文标题】:How to find out if a client connection is terminated within a TCP socket connection in a java server?如何确定客户端连接是否在 Java 服务器的 TCP 套接字连接中终止? 【发布时间】:2021-08-15 10:07:11 【问题描述】:

我正在使用 TCP 套接字上的阻塞读/写实现多线程服务器,其中 InputStream 和 OutputStream 原语由适当的 Reader/Writer 封装。

如果客户端断开连接,InputStreamReader's read() 方法会返回-1,但如果客户端连接完好无损,它会无限期地等待。我该如何克服这个问题?

【问题讨论】:

“客户端连接在 TCP 套接字连接中丢失”是没有意义的。不清楚您在问什么,除非您要查找的是套接字读取超时。 抱歉造成误会。在我的例子中,客户端有一个超时,之后操作终止。 ruby 客户端有一个围绕这个函数调用的 Timeout::timeout。 那么你的问题是什么?超时跳闸-> 出了点问题。可能是网络或慢速服务器或其他一些问题,但这是您唯一可用的机制。 NB "连接在一个 TCP 连接内终止" 也是没有意义的。 【参考方案1】:

这里最重要的问题是“丢失”对于应用程序的真正含义是什么。套接字通信中有两个角色——写者和读者。我们的代码可能同时扮演这两个角色。因此,对于套接字通信:

    从作者的角度来看,连接“丢失”意味着作者在写入/发送到套接字时出错(OutputStream.write(...))。对于 TCP,这仅由于接收到 FIN 或 RST,或由于重新传输超时(在 Java 中无法管理)或由于操作系统检测到 TCP keepalive 连接丢失(https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html)而发生

    从读者的角度来看,连接“丢失”意味着读者在从套接字读取时出错(InputStream.read(...))。对于 TCP,这仅由于接收到 FIN 或 RST,或者由于操作系统检测到与 TCP keepalive 的“丢失”连接,或者由于阻塞读取超时 (SO_TIMEOUT) 而发生。

TCP 重传超时 (RTO) 和 TCP keepalive 都不太好:

    RTO:https://pracucci.com/linux-tcp-rto-min-max-and-tcp-retries2.html Keepalive 更糟糕:https://docs.microsoft.com/en-us/windows/win32/winsock/so-keepalive。默认情况下,Windows 在 2 小时后发送第一个 TCP Keepalive 数据包(!)。 Linux 上的相同问题https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html

因此,如果您是服务器,我强烈建议您只使用强制设置 SO_TIMEOUT(或您自己的用于解除阻塞读取的等效项),或者在应用程序级别引入运行状况/可用性检查。有两种典型的模式:ping-pong(达到超时后,服务器向客户端发送一个 ping 数据包并等待接收到的 pong),一个心跳(客户端应该在指定的周期内发送一个心跳包)时间)。请注意,客户端通常会遇到相同的问题,您可能会决定支持双方的 ping-pong/heartbeat。

只是一个关于主题https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/的有趣帖子

【讨论】:

【参考方案2】:

您是否在套接字对象上创建输入流?

ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

然后你可以检查套接字是否连接使用

socket.isClosed()

在你的循环之间?

【讨论】:

你似乎把魔法属性归于isClosed()。它不像你认为的那样做。它最初是假的,当 you 关闭套接字时变为真,并且在其他任何时候都不会:具体来说,当 peer 断开连接或连接时它不会神奇地变为真被中止。如果你想替换isConnected(),那也行不通,出于同源的原因。您还没有尝试过。

以上是关于如何确定客户端连接是不是在 Java 服务器的 TCP 套接字连接中终止?的主要内容,如果未能解决你的问题,请参考以下文章

如何确定客户端是不是使用 HTTP/2 进行连接?

如何确定是啥在重置我的连接?

如何确定 JMS 连接是不是存在?

java socket 客户端设置未接收到数据的超时问题

如何确定与 SignalR 客户端的服务器断开连接?

如何在不关闭 TCP 连接的情况下关闭处理 TCP 请求的线程?