检查ClientSocket是否已在java中断开连接

时间:2009-03-30 20:51:58

标签: java sockets

这是一个跟进:

this question

基本上,我有一个服务器循环来管理与一个单独客户端的连接。在循环中的某一点,如果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
}

问题是,一旦创建了套接字,应用程序将挂起读取,我假设等待永远不会发生的数据,因为客户端永远不会发送到服务器。在此之前没问题,因为这正确处理断开连接(当客户端断开连接时读取最终会失败)并且循环将尝试重新建立连接。但是,我现在已经添加了上面的sendHeartBeat()方法,它定期让客户端知道服务器仍在运行。如果读取持有线程,则心跳永远不会发生!

所以,我假设我正在测试连接是否仍然不正确。我可以,作为一个快速的黑客,在一个单独的线程中运行bufferedReader.read(),但随后我将有各种各样的并发问题,我真的不想处理。

所以这个问题有几个问题: 1)我是否正确检查客户端断开连接? 2)如果没有,我该怎么办? 3)如果我正确地这样做我怎么做我读取不让流程人质?或者是穿线唯一的方式?

3 个答案:

答案 0 :(得分:9)

创建套接字时,首先设置超时:

private int timeout    = 10000;
private int maxTimeout = 25000;

clientSocket.setSoTimeout(timeout);

有了这个,如果读取超时,你将获得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;
}

处理此问题的常用方法是将超时设置为某个数字(例如10秒),然后跟踪上次成功读取套接字的时间。如果已超过2.5倍的超时,则放弃客户端并关闭套接字(因此发送FIN数据包到另一方,以防万一)。

如果心跳从远程系统获得任何响应,但只是在连接断开时最终生成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
}

答案 1 :(得分:1)

您正在正确检查,您可以添加带有IOException的try catch,以防它发生。

有一种方法可以避免线程,你可以使用带有非bloking套接字的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
   }
}

答案 2 :(得分:0)

您应该在断开连接检测中添加错误检查。有时,当与另一端的连接丢失时,可能会抛出IOException。

我担心这里的线程是不可避免的。如果您不想阻止代码的执行,则需要创建一个单独的线程。