在 java 中检查 ClientSocket 是否已断开连接挂起
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/698964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Checking if a ClientSocket has disconnected in java hangs
提问by Alex
This is a follow up to:
这是对以下内容的跟进:
Basically, I have a server loop that manages a connection to one solitary client. At one point in the loop, if a ClientSocket exists it attempts a read to check if the client is still connected:
基本上,我有一个服务器循环来管理与一个单独客户端的连接。在循环中的某一点,如果 ClientSocket 存在,它会尝试读取以检查客户端是否仍处于连接状态:
if (bufferedReader.read()==-1 ) {
logger.info("CONNECTION TERMINATED!");
clientSocket.close();
setUpSocket(); //sets up the server to reconnect to the client
}else{
sendHeartBeat(); //Send a heartbeat to the client
}
The problem is, that once a socket has been created the application will hang on the read, I assume waiting for data that will never come, since the client never sends to the server. Before this was OK, because this correctly handled disconnects (the read would eventually fail when the client disconnected) and the loop would attempt reestablish the connection. However, I now have added the above sendHeartBeat() method, which periodically lets the client know the server is still up. If the read is holding the thread then the heartbeats never happen!
问题是,一旦创建了套接字,应用程序将挂起读取,我假设等待永远不会到来的数据,因为客户端永远不会发送到服务器。在这之前没问题,因为这正确处理了断开连接(当客户端断开连接时读取最终会失败)并且循环将尝试重新建立连接。但是,我现在添加了上面的 sendHeartBeat() 方法,它会定期让客户端知道服务器仍在运行。如果读取保持线程,则心跳永远不会发生!
So, I assume I am testing if the connection is still up incorrectly. I could, as a quick hack, run the bufferedReader.read() in a seperate thread, but then I'll have all sorts of concurrency issues that I really don't want to deal with.
所以,我假设我正在测试连接是否仍然不正确。作为一个快速的黑客,我可以在一个单独的线程中运行 bufferedReader.read(),但是我会遇到各种我真的不想处理的并发问题。
So the question is a few fold: 1) Am I checking for a client disconnect correctly? 2) If not, how should I do it? 3) If I am doing it correctly how I do I get the read to not hold the process hostage? Or is threading the only way?
所以问题有几个方面:1)我是否正确检查客户端断开连接?2)如果没有,我该怎么做?3)如果我做对了,我如何获得阅读以不扣留进程?或者线程是唯一的方法?
采纳答案by Eddie
When you create your socket, first set a timeout:
创建套接字时,首先设置超时:
private int timeout = 10000;
private int maxTimeout = 25000;
clientSocket.setSoTimeout(timeout);
With this, if a read times out you'll get java.net.SocketTimeoutException
(which you have to catch). Thus, you could do something like this, assuming you've previously set the SO_TIMEOUT as shown above, and assuming that the heartbeat will always get a response from the remote system:
有了这个,如果读取超时,你会得到java.net.SocketTimeoutException
(你必须抓住)。因此,你可以做这样的事情,假设你之前已经设置了 SO_TIMEOUT 如上所示,并假设心跳总是从远程系统得到响应:
volatile long lastReadTime;
try {
bufferedReader.read();
lastReadTime = System.currentTimeMillis();
} catch (SocketTimeoutException e) {
if (!isConnectionAlive()) {
logger.info("CONNECTION TERMINATED!");
clientSocket.close();
setUpSocket(); //sets up the server to reconnect to the client
} else {
sendHeartBeat(); //Send a heartbeat to the client
}
}
public boolean isConnectionAlive() {
return System.currentTimeMillis() - lastReadTime < maxTimeout;
}
A common way of handling this is setting the timeout to some number (say 10 seconds) and then keeping track of the last time you successfully read from the socket. If 2.5 times your timeout have elapsed, then give up on the client and close the socket (thus sending a FIN packet to the other side, just in case).
处理此问题的常用方法是将超时设置为某个数字(例如 10 秒),然后跟踪上次成功从套接字读取的时间。如果超时已经过去了 2.5 倍,则放弃客户端并关闭套接字(从而向另一端发送一个 FIN 数据包,以防万一)。
If the heartbeat will notget any response from the remote system, but is just a way of ultimately generating an IOException earlier when the connection has fallen down, then you could do this (assuming that the sendHeartBeat itself will not throw an IOException):
如果心跳不会从远程系统得到任何响应,而只是在连接中断时最终生成 IOException 的一种方式,那么您可以这样做(假设 sendHeartBeat 本身不会抛出 IOException):
try {
if (bufferedReader.read() == -1) {
logger.info("CONNECTION TERMINATED with EOF!");
resetConnection();
}
} catch (SocketTimeoutException e) {
// This just means our read timed out ... the socket is still good
sendHeartBeat(); //Send a heartbeat to the client
} catch (IOException e) {
logger.info("CONNECTION TERMINATED with Exception " + e.getMessage());
resetConnection();
}
....
private void resetConnection() {
clientSocket.close();
setUpSocket(); //sets up the server to reconnect to the client
}
回答by kgiannakakis
You should add error checking in your disconnection detection. Sometimes an IOException may be thrown when the connection to the other end is lost.
您应该在断开连接检测中添加错误检查。有时,当与另一端的连接丢失时,可能会抛出 IOException。
I am afraid that threading is unavoidable here. If you don't want to block the execution of your code, you need to create a separate thread.
恐怕在这里穿线是不可避免的。如果不想阻塞代码的执行,则需要创建一个单独的线程。
回答by no_ripcord
You are checking correctly, you can should add a try catch with IOException in case it occurs.
您正在正确检查,您应该添加一个带有 IOException 的 try catch 以防它发生。
There is a way to avoid threading, you can use a Selector with a non-bloking socket.
有一种方法可以避免线程化,您可以使用带有非阻塞套接字的 Selector。
public void initialize(){
//create selector
Selector selector = Selector.open();
ServerSocketChannel acceptSocket = ServerSocketChannel.open();
acceptSocket.configureBlocking(false);
String bindIp = "127.0.0.1";
int bindPort = 80;
acceptSocket.socket().bind(new InetSocketAddress(bindIp, bindPort));
//register socket in selector for ACCEPT operation
acceptSocket.register(selector, SelectionKey.OP_ACCEPT);
this.selector = selector;
this.serverSocketChannel = serverSocketChannel;
}
public void serverStuff() {
selector.select(maxMillisecondsToWait);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
if( selectedKeys.size() > 0 )
{
if( key.isAcceptable() ){
//you can accept a new connection
SocketChannel clientSk = serverSocketChannel.accept();
clientSk.configureBlocking(false);
//register your SocketChannel in the selector for READ operations
clientSk.register(selector, SelectionKey.OP_READ);
} else if( key.isReadable() ){
//you can read from your socket.
//it will return you -1 if the connection has been closed
}
}
if( shouldSendHeartBeat() ){
SendHeartBeat
}
}