检查 Java TCP 服务器上的客户端断开连接 - 仅输出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5263565/
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 for a client disconnect on a Java TCP server - output only
提问by Myn
I have a Java TCP server which, when a client connects to it, outputs a message to the client every 30 seconds. It is a strict requirement that the client does not send any messages to the server, and that the server does not send any data other than the 30-second interval messages to the client.
我有一个 Java TCP 服务器,当客户端连接到它时,它每 30 秒向客户端输出一条消息。严格要求客户端不向服务器发送任何消息,服务器不向客户端发送除 30 秒间隔消息以外的任何数据。
When I disconnect the client, the server will not realise this until the next time it tries to write to the client. So it can take up to 30 seconds for the server to recognise the disconnect.
当我断开客户端连接时,服务器直到下一次尝试写入客户端时才会意识到这一点。因此,服务器识别断开连接可能需要长达 30 秒的时间。
What I want to do is check for the disconnect every few seconds without having to wait, but I am not sure how to do this given that a) the server does not receive from the client and b) the server cannot send any other data. Would anyone please be able to shed some light on this? Thanks.
我想要做的是每隔几秒钟检查一次断开连接,而不必等待,但我不知道如何做到这一点,因为 a) 服务器没有从客户端接收到 b) 服务器无法发送任何其他数据。请问有人能对此有所了解吗?谢谢。
回答by Brian Roach
Even though your server doesn't "receive" from the client, a non-blocking read on the client socket will tell you that either there's nothing to be read (as you expect), or that the client has disconnected.
即使您的服务器没有从客户端“接收”,客户端套接字上的非阻塞读取也会告诉您没有任何内容可读取(如您所料),或者客户端已断开连接。
If you're using NIO you can simply use a non-blocking Selector
loop (with non-blocking sockets) and only write on your 30 second marks. If a SelectionKey
is readable and the read on the SocketChannel
returns -1 you know the client has disconnected.
如果您使用 NIO,您可以简单地使用非阻塞Selector
循环(使用非阻塞套接字)并且只在 30 秒标记上写入。如果 aSelectionKey
可读并且读取SocketChannel
返回 -1,则您知道客户端已断开连接。
EDIT:Another approach with blocking is simply to select with a 30 second timeout. Any client disconnects will cause the select to return and you'll know which ones those are via the read set. The additional thing you'd need to do there is track how long you were blocked in the select to figure out when to do your writes on the 30 second mark (Setting the timeout for the next select to the delta).
编辑:另一种阻塞方法是简单地选择 30 秒超时。任何客户端断开连接都会导致选择返回,您将通过读取集知道哪些是那些。您需要做的另一件事是跟踪您在选择中被阻止的时间,以确定何时在 30 秒标记上进行写入(将下一次选择的超时设置为增量)。
Big Edit:After talking to Myn below, offering complete example:
大编辑:在与下面的 Myn 交谈后,提供完整的示例:
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(1);
}
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
// Set a 1 second timeout on the socket
clientSocket.setSoTimeout(1000);
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
long myNextOutputTime = System.currentTimeMillis() + 30000;
String inputLine = null;
boolean connected = true;
while (connected)
{
try {
inputLine = in.readLine();
if (inputLine == null)
{
System.out.println("Client Disconnected!");
connected = false;
}
}
catch(java.net.SocketTimeoutException e)
{
System.out.println("Timed out trying to read from socket");
}
if (connected && (System.currentTimeMillis() - myNextOutputTime > 0))
{
out.println("My Message to the client");
myNextOutputTime += 30000;
}
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
Worth noting here is that the PrintWriter
really moves you far away from the actual socket, and you're not going to catch the socket disconnect on the write (It will never throw an exception, you have to manually check it with checkError()
) You could change to using a BufferedWriter
instead (requires using flush()
to push the output) and handling it like the BufferedReader
to catch a disco on the write.
这里值得注意的是,PrintWriter
真正使您远离实际套接字,并且您不会在写入时捕获套接字断开连接(它永远不会抛出异常,您必须手动检查它checkError()
)您可以更改为使用 aBufferedWriter
代替(需要使用flush()
来推送输出)并像在BufferedReader
写入时捕捉迪斯科一样处理它。
回答by Tayyab
If you are managing multiple clients then I guess you would be using Non-Blocking sockets (If not then consider using Non-Blocking). You can use Selector to monitor all the connected sockets to check if they are readable or writeable or there is some Error on that socket. When some client disconnects, your Selector will mark that socket and will return. For more help google "Socket Select function"
如果您正在管理多个客户端,那么我猜您会使用非阻塞套接字(如果不是,则考虑使用非阻塞)。您可以使用 Selector 监视所有连接的套接字以检查它们是否可读或可写,或者该套接字上是否存在错误。当某些客户端断开连接时,您的选择器将标记该套接字并返回。如需更多帮助,请使用谷歌“套接字选择功能”