如何确定客户端连接是不是在 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 套接字连接中终止?的主要内容,如果未能解决你的问题,请参考以下文章