客户端断开连接后,Socket isClosed() 返回 false。

6

我在我的Java应用程序中有一个TCP服务器,它将我的应用程序生成的数据发送到所有连接的客户端。对于每个新的套接字,我都会创建一个新的线程。以下是线程代码。

    public void run() {
        try {
            PrintStream printStream = new PrintStream(socket.getOutputStream(), true);
            while (true) {
                if (socket.isClosed()) {
                    break;
                }
                synchronized (DataSource.getInstance()) {
                    printStream.println(DataSource.getInstance().getData());
                    try {
                        DataSource.getInstance().wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

当新数据可用时,数据生成线程会写入DataSoure并调用notifyAll(),以便处理连接的套接字的所有线程都会醒来并将可用数据发送给客户端。

我的问题是,即使客户端断开连接,socket.isClosed()也会返回true。因此,处理套接字的线程永远不会被终止。

为什么会发生这种情况?如何在客户端断开连接时退出线程?

2个回答

10

TCP并不存在"断开"。一个对等方只能表明它不想再发送任何东西(发送FIN),但这并不意味着它不想接收任何东西。另一端只有在尝试读取数据并得到RST响应后才会意识到不需要读了。

如果你把"不想读取"视为客户端关闭连接的指示(如果应用程序协议是这样的话),那么你可以通过从套接字中读取来测试对等方是否已经"断开"。系统调用将返回没有错误但是0字节,这意味着将不再有来自对等方的数据。在Java中,这种情况被转换为EOFException异常。

除此之外,套接字不会自动关闭 - 只能显式关闭。isClosed只有在本地端口显式关闭套接字时才会返回true,而不是对等端口关闭时。isConnected也不会告诉您另一端是否已经"断开",它只告诉您自己是否已连接到对等点。

参见Java Socket API: How to tell if a connection has been closed?


我无法从套接字中读取数据,因为客户端没有发送任何数据。我阅读了您指出的SO答案,我认为当我不断尝试写入已关闭的对等套接字时,最终会得到一个IOException。我是正确的吗? - Lahiru Chandima
如果客户端只发送了空数据,那么连接仍然保持打开状态。客户端通过关闭连接来明确表示不想再发送任何数据。这个信息是通过使用FIN标志传输的,然后读取操作将不会阻塞,而是指示EOF(文件结束)。如果对等方的操作系统崩溃或系统断开连接,则不会发送FIN,此时仍然假定连接处于连接状态。 - Steffen Ullrich
实际上,我断开连接的方法是杀死对等端。根据您的说法,连接将被认为已连接。有没有办法检测客户端是否已经停止工作,以便我可以退出线程? - Lahiru Chandima
如果对等进程被杀死,大多数操作系统将关闭连接,即发送FIN并检测EOF。如果您想检测到连接中断(电缆断开等),则需要使用TCP keep-alive或具有应用程序特定的keep alive协议,或在一段时间内无活动后关闭连接。 - Steffen Ullrich
但是我不能从套接字中读取数据来判断它是否已连接。由于客户端不发送任何数据,如果我在客户端仍然存活时尝试读取数据,它将会被阻塞。这样我就无法将生成的数据发送给客户端了。 - Lahiru Chandima
你将会通过编写来检测客户端是否关闭。我真的建议阅读我链接的帖子以及这篇帖子上的讨论。 - Steffen Ullrich

-1

在编程中,你应该使用isConnected()函数来检测远程套接字是否关闭,而不是使用isClosed()函数。


1
这样做不行。Java文档已经明确说明了这一点:“注意:关闭套接字并不清除其连接状态,这意味着如果在关闭之前成功连接,则此方法将对已关闭的套接字(请参见isClosed())返回true。” - Jing He

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接