Java套接字API:如何判断连接是否已关闭?

时间:2012-04-20 05:20:59

标签: java sockets networking client-server

我遇到了Java套接字API的一些问题。我正在尝试显示当前连接到我的游戏的玩家数量。很容易确定玩家何时连接。但是,确定播放器何时使用套接字API断开连接似乎不必要。

在远程断开连接的套接字上调用isConnected()似乎总是返回true。同样,在已远程关闭的套接字上调用isClosed()似乎总是返回false。我已经读过,要实际确定套接字是否已关闭,必须将数据写入输出流并且必须捕获异常。这似乎是处理这种情况的一种非常不洁净的方式。我们不得不经常通过网络向垃圾邮件发送垃圾邮件,以便知道套接字何时关闭。

还有其他解决方案吗?

10 个答案:

答案 0 :(得分:153)

没有TCP API会告诉您当前的连接状态。 isConnected()isClosed()告诉您套接字的当前状态 。不一样。

  1. isConnected()告诉您是否已连接此套接字。你有,所以它返回true。

  2. isClosed()告诉您是否已关闭此套接字。在您拥有之前,它会返回false。

  3. 如果 peer 已按顺序关闭连接

    • read()返回-1
    • readLine()返回null
    • readXXX()会为任何其他XXX投放EOFException

    • 写入将抛出IOException:'由peer重置连接',最终会受到缓冲延迟的影响。

  4. 如果由于任何其他原因导致连接中断,则写入将最终抛出IOException,如上所述,并且读取可能会执行相同的操作。

  5. 如果对等方仍然连接但未使用连接,则可以使用读取超时。

  6. 与您在其他地方阅读的内容相反,ClosedChannelException并未告诉您这一点。 [同时SocketException: socket closed.]它只会告诉您关闭频道,然后继续使用它。换句话说,您的编程错误。它不表示已关闭的连接。

  7. 在Windows XP上使用Java 7进行的一些实验结果显示,如果:

    • 您选择OP_READ
    • select()返回大于零的值
    • 关联的SelectionKey已无效(key.isValid() == false

    表示对等方已重置连接。然而,这可能是JRE版本或平台所特有的。

答案 1 :(得分:8)

各种消息传递协议的一般做法是保持彼此的心跳(继续发送ping数据包),数据包不需要非常大。探测机制将允许您检测断开连接的客户端,甚至在TCP一般计算出来之前(TCP超时远高)发送探测并等待5秒钟回复,如果您没有看到回复说2-3后续探测,您的播放器已断开连接。

此外,related question

答案 2 :(得分:2)

我看到其他答案刚刚发布,但我认为你是与玩游戏的客户互动的,所以我可能会提出另一种方法(而BufferedReader在某些情况下绝对有效)。

如果您想......您可以将“注册”责任委托给客户。即你会有一组连接的用户,每个收到的消息都有一个时间戳...如果客户超时,你会强制重新注册客户端,但这会导致下面的引用和想法。

  

我已经读过这个以确定套接字是否有   已关闭的数据必须写入输出流和异常   必须抓住。这似乎是一种非常不洁净的方式来处理这个问题   情况。

如果你的Java代码没有关闭/断开Socket,那么你会怎么通知远程主机关闭你的连接?最终,你的try / catch与调查ACTUAL套接字上的事件的poller大致相同。请考虑以下事项:

  • 您的本地系统可以在不通知您的情况下关闭您的套接字......这只是Socket的实现(即它不会轮询硬件/驱动程序/固件/状态更改)。
  • new Socket(代理p)......有多方(真正有6个端点)可以关闭你的连接......

我认为抽象语言的一个特点就是你从细节中抽象出来。想想C#中的using关键字(尝试/最后)对于SqlConnection或者其他什么......这只是做生意的成本......我认为try / catch / finally是Socket使用的可接受和必要的模式。

答案 3 :(得分:1)

我认为这是tcp连接的本质,在标准中,在我们断定连接消失之前,传输中需要大约6分钟的静默! 所以我认为你无法找到解决这个问题的确切方法。也许更好的方法是编写一些方便的代码来猜测服务器何时应该假设用户连接已关闭。

答案 4 :(得分:0)

正如@ user207421所说,由于TCP / IP协议体系结构模型,无法知道连接的当前状态。因此,服务器必须在关闭连接之前通知您,否则您必须自己进行检查。
这是一个简单的示例,显示了如何知道服务器已关闭套接字:

sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null) 
      log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");

答案 5 :(得分:0)

我遇到了类似的问题。在我的情况下,客户端必须定期发送数据。希望您有同样的要求。然后,我设置了SO_TIMEOUT socket.setSoTimeout(1000 * 60 * 5);,该时间在指定时间到期时抛出java.net.SocketTimeoutException。然后,我可以轻松地检测到死客户端。

答案 6 :(得分:0)

这里是任何数据类型的另一种通用解决方案。

int offset = 0;
byte[] buffer = new byte[8192];

try {
    do {
        int b = inputStream.read();

        if (b == -1)
           break;

        buffer[offset++] = (byte) b;

        //check offset with buffer length and reallocate array if needed
    } while (inputStream.available() > 0);
} catch (SocketException e) {
    //connection was lost
}

//process buffer

答案 7 :(得分:-1)

那就是我的处理方式

 while(true) {
        if((receiveMessage = receiveRead.readLine()) != null ) {  

        System.out.println("first message same :"+receiveMessage);
        System.out.println(receiveMessage);      

        }
        else if(receiveRead.readLine()==null)
        {

        System.out.println("Client has disconected: "+sock.isClosed()); 
        System.exit(1);
         }    } 

如果result.code == null

答案 8 :(得分:-3)

在Linux上写入()进入一个套接字,另一边,你不知道,关闭将引发一个SIGPIPE信号/异常,但你想调用它。但是,如果您不想被SIGPIPE捕获,您可以使用带有标志MSG_NOSIGNAL的send()。 send()调用将以-1返回,在这种情况下,你可以检查errno,它会告诉你你试图写一个破坏的管道(在这种情况下是一个套接字),其值为EPIPE,根据errno.h相当于32.作为对EPIPE的反应,您可以加倍回来并尝试重新打开套接字并尝试再次发送您的信息。

答案 9 :(得分:-4)

我首先检查套接字当前是否为NULL,然后检查它是否未关闭

if(socket!=null && !socket.isClosed()){
//good to go
}
相关问题