我在TCP套接字上使用阻塞读/写来实现一个多线程服务器,InputStream和OutputStream原语由适当的读/写器封装。
如果客户端断开连接,则InputStreamReader's read()
方法返回-1
,但如果客户端连接完好无损,则方法会无限期等待。我该如何克服这一点?
这里最重要的问题是"丢失";对应用程序来说真的很重要。套接字通信中有两个角色——编写器和读取器。我们的代码可能同时扮演这两个角色。因此,对于套接字通信:
-
从作者的角度来看;丢失";意味着写入程序在写入/发送到套接字(
OutputStream.write(...)
(时出错。对于TCP,这种情况仅由于接收FIN或RST,或由于重传超时(在Java中不可用以进行管理(,或由于操作系统检测到TCP保持活动时连接丢失而发生(https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) -
从读者的角度来看;丢失";意味着读取器在从套接字读取时出错(
InputStream.read(...)
(。对于TCP来说,这仅仅是由于接收FIN或RST,或者由于OS已经检测到"发送";丢失";TCP保持活动的连接,或由于阻塞读取超时(SO_timeout(。
TCP重传超时(RTO(和TCP保活工作都不太好:
- RTO:https://pracucci.com/linux-tcp-rto-min-max-and-tcp-retries2.html
- 保活是最糟糕的: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()
在你的循环之间?