javasocketapi:如何判断连接是否已关闭?

qmb5sa22  于 2021-06-30  发布在  Java
关注(0)|答案(9)|浏览(482)

我在使用javasocketapi时遇到了一些问题。我正在尝试显示当前连接到我的游戏的玩家数。很容易确定玩家何时连接。然而,使用socketapi来确定播放器何时断开了连接似乎是不必要的困难。
打电话 isConnected() 在远程断开的插座上,似乎总是会返回 true . 同样,打电话 isClosed() 在远程关闭的套接字上,似乎总是返回 false . 我已经读到,要真正确定套接字是否已关闭,必须将数据写入输出流并捕获异常。这似乎是一个非常不干净的方式来处理这种情况。我们只需要不断地通过网络发送垃圾信息,就可以知道套接字何时关闭。
还有别的解决办法吗?

eagi6jfj

eagi6jfj1#

没有tcp api可以告诉您连接的当前状态。 isConnected() 以及 isClosed() 告诉你插座的当前状态。不是一回事。 isConnected() 告诉您是否已连接此插座。你有,所以它返回真的。 isClosed() 告诉您是否已关闭此套接字。直到你有,它返回假。
如果对等方已经有序地关闭了连接 read() 返回-1 readLine() 退货
null readXXX() 投掷 EOFException 对于任何其他。
写一篇文章就会抛出一篇文章 IOException :“对等端重置连接”,最终会受到缓冲延迟的影响。
如果由于任何其他原因连接已断开,写入操作将抛出 IOException ,最终,如上所述,read可能会做同样的事情。
如果对等方仍然连接但未使用连接,则可以使用读取超时。
与你在别处看到的相反, ClosedChannelException 不会告诉你的[我也不知道 SocketException: socket closed. ]它只告诉你你关闭了频道,然后继续使用它。换言之,是你的程序错误。它不表示闭合连接。
在windows xp上使用java 7进行的一些实验表明,如果:
您正在选择
OP_READ select() 返回大于零的值
关联的 SelectionKey 已无效( key.isValid() == false )
这意味着对等机已重置连接。但是,这可能是jre版本或平台所特有的。

qlfbtfca

qlfbtfca2#

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

v09wglhw

v09wglhw3#

我就是这么处理的

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

wgx48brx

wgx48brx4#

我也面临同样的问题。在我的情况下,客户必须定期发送数据。我希望你也有同样的要求。然后我设置了超时 socket.setSoTimeout(1000 * 60 * 5); 哪个是投掷 java.net.SocketTimeoutException 指定时间到期时。这样我就可以很容易地发现死客户。

axkjgtzd

axkjgtzd5#

在各种消息传递协议中,保持彼此心跳(保持发送ping数据包)是通常的做法,数据包不需要很大。探测机制将允许您检测到断开连接的客户端,甚至在tcp发现之前(tcp超时要高得多)发送探测并等待5秒钟等待答复,如果您没有看到后续2-3次探测的答复,则您的播放机已断开连接。
还有,相关问题

bnlyeluc

bnlyeluc6#

正如@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 !");
0yg35tkg

0yg35tkg7#

我看到了刚刚发布的另一个答案,但我认为您与玩游戏的客户交互,因此我可能会提出另一种方法(而bufferedreader在某些情况下肯定是有效的)。
如果你想。。。您可以将“注册”责任委托给客户。i、 你将拥有一个连接用户的集合,每个用户收到的最后一条消息都有一个时间戳。。。如果客户超时,您将强制客户重新注册,但这会导致下面的引用和想法。
我已经读到,要真正确定套接字是否已关闭,必须将数据写入输出流并捕获异常。这似乎是一个非常不干净的方式来处理这种情况。
如果java代码没有关闭/断开套接字,那么如何通知您远程主机关闭了连接?最终,try/catch所做的工作与轮询器在实际套接字上侦听事件所做的工作大致相同。考虑以下几点:
您的本地系统可以关闭您的套接字而不通知您。。。这只是socket的实现(即,它不轮询硬件/驱动程序/固件/任何状态更改)。
新建套接字(proxy p)。。。有多个参与方(实际上有6个端点)可能正在关闭您的连接。。。
我认为抽象语言的一个特点是你是从细节中抽象出来的。考虑一下在c#(try/finally)中使用关键字来表示SQLConnections或其他什么。。。只是做生意的成本。。。我认为try/catch/finally是socket使用的公认且必要的模式。

wn9m85ua

wn9m85ua8#

在linux上,当写()到一个套接字中时,另一方(您不知道)关闭了,将触发一个sigpipe信号/异常,不管您如何调用它。但是,如果不想被sigpipe发现,可以使用send()和标志msg\u nosignal。send()调用将返回-1,在本例中,您可以检查errno,它将告诉您试图用epipe值编写一个断开的管道(在本例中是一个套接字),根据errno.h,epipe值等于32。作为对epipe的一种React,您可以返回并尝试重新打开套接字,然后再次发送您的信息。

fzsnzjdm

fzsnzjdm9#

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

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

相关问题