如何查明客户端连接是否在java服务器中的TCP套接字连接中终止



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

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

这里最重要的问题是"丢失";对应用程序来说真的很重要。套接字通信中有两个角色——编写器和读取器。我们的代码可能同时扮演这两个角色。因此,对于套接字通信:

  1. 从作者的角度来看;丢失";意味着写入程序在写入/发送到套接字(OutputStream.write(...)(时出错。对于TCP,这种情况仅由于接收FIN或RST,或由于重传超时(在Java中不可用以进行管理(,或由于操作系统检测到TCP保持活动时连接丢失而发生(https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html)

  2. 从读者的角度来看;丢失";意味着读取器在从套接字读取时出错(InputStream.read(...)(。对于TCP来说,这仅仅是由于接收FIN或RST,或者由于OS已经检测到"发送";丢失";TCP保持活动的连接,或由于阻塞读取超时(SO_timeout(。

TCP重传超时(RTO(和TCP保活工作都不太好:

  1. RTO:https://pracucci.com/linux-tcp-rto-min-max-and-tcp-retries2.html
  2. 保活是最糟糕的:https://learn.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数据包,等待收到乒乓球(、心跳(客户端应在指定的时间段内发送心跳数据包(。请注意,客户端经常遇到相同的问题,您可能会决定为双方支持乒乓球/心跳。

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

您是否通过套接字对象创建输入流?

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

然后您可以使用检查插座是否连接

socket.isClosed()

在你的循环之间?

最新更新