【问题标题】:TCP detect disconnected server from clientTCP 检测与客户端断开连接的服务器
【发布时间】:2014-04-07 10:37:25
【问题描述】:

我正在用 Java 编写一个简单的 TCP 客户端/服务器程序对,如果客户端在 10 秒内没有发送任何内容,则服务器必须断开连接。 socket.setSoTimeout() 让我明白了,服务器断开连接就好了。问题是 - 我怎样才能让客户端确定服务器是否关闭?目前我正在使用 DataOutputStream 写入服务器,这里的一些答案表明写入关闭的套接字会抛出 IOException,但这不会发生。

我应该使用哪种写入器对象来向服务器发送任意字节块,这会引发异常或以其他方式指示连接已远程关闭?

编辑:这是客户端代码。这是一个测试函数,它从文件系统中读取一个文件并将其发送到服务器。它以块的形式发送它,并在每个块之间暂停一段时间。

public static void sendFileWithTimeout(String file, String address, int dataPacketSize, int timeout) {
    Socket connectionToServer = null;
    DataOutputStream outStream = null;
    FileInputStream inStream = null;
    try {
        connectionToServer = new Socket(address, 2233);

        outStream = new DataOutputStream(connectionToServer.getOutputStream());

        Path fileObject = Paths.get(file);

        outStream.writeUTF(fileObject.getFileName().toString());

        byte[] data = new byte[dataPacketSize];

        inStream = new FileInputStream(fileObject.toFile());
        boolean fileFinished = false;
        while (!fileFinished) {
            int bytesRead = inStream.read(data);
            if (bytesRead == -1) {
                fileFinished = true;
            } else {
                outStream.write(data, 0, bytesRead);
                System.out.println("Thread " + Thread.currentThread().getName() + " wrote " + bytesRead + " bytes.");
                Thread.sleep(timeout);
            }
        }

    } catch (IOException | InterruptedException e) {
        System.out.println("Something something.");
        throw new RuntimeException("Problem sending data to server.", e);
    } finally {
        TCPUtil.silentCloseObject(inStream);
        TCPUtil.silentCloseObject(outStream);
        TCPUtil.silentCloseObject(connectionToServer);
    }
}

我希望 outStream.write 在尝试写入已关闭的服务器时抛出 IOException,但什么也没有。

【问题讨论】:

  • “但这不会发生”。那么服务器关闭时发生了什么?
  • 当服务器关闭套接字时,您的客户端输入流将指示该流已关闭。如果你碰巧在服务器关闭套接字的同时写入输出流,你也应该得到一个异常。不知道为什么你没有得到例外。你能显示一些代码吗?
  • @CHOCKO - 几乎没有。客户端将数据写入流并正常退出,而不关心服务器不再在那里读取数据。

标签: java tcp stream


【解决方案1】:

我希望 outStream.write 在尝试写入已关闭的服务器时抛出 IOException,但什么也没有。

第一次不会这样做,因为套接字发送缓冲区。如果你继续写,它最终会抛出一个 IOException: 'connection reset'。如果您没有数据可以到达该点,您将永远不会发现对等点已关闭。

【讨论】:

  • 这适用于所有写作方法吗?还是可以通过使用 DataOutputStream 以外的方法来解决?
  • @user1638190 (a) 是 (b) 否。
【解决方案2】:

我认为你需要在像 outStream.flush(); 这样写之后flushclose outStream.close(); inStream.close();

【讨论】:

  • 没有。 close() 意味着 flush(),关闭一个 Socket 的输入或输出流都会关闭另一个流和 Socket。
【解决方案3】:

记住ServerSocket.setSoTimeout() 不同于客户端的同名函数。

对于服务器,这个函数只抛出SocketTimeoutException,如果超时过期,你可以捕捉它,但服务器套接字仍然存在。

对于客户端,setSoTimeout() 与流读取的“读取超时”相关。

在您的情况下,您必须在捕获SocketTimeoutException => 后显示关闭连接的套接字的服务器代码 => 确保服务器关闭与指定客户端的关联套接字。 如果完成,在客户端,您的代码行:

throw new RuntimeException("Problem sending data to server.", e);

将被调用。

[更新]

我注意到您声明将服务器端接受套接字的超时设置为 10 秒(=10,000 毫秒);在此期间,您的客户是否完成了所有文件发送?如果确实如此,则永远不会发生异常。

[建议]

为了探测,只需注释掉你的读取文件内容的代码发送到服务器,并尝试用几行写入输出流替换:

outStream.writeUTF("ONE");
outStream.writeUTF("TWO");
outStream.writeUTF("TREE");

那么你就可以得出结论了。

【讨论】:

  • ServerSocket 没有关闭 - 它仍然可以与其他潜在客户一起工作。服务器的 Socket 对象(从这个特定客户端接收数据的对象)是一个设置了超时并在没有收到数据时关闭的对象。
  • 我在服务器接受客户端连接时提到的套接字是client socket(它相当于客户端的套接字,但具有恢复读/写方向)。你怎么知道它在你的服务器端正确关闭了?
  • 这只是更多的混乱。在ServerSocket 上设置超时会导致accept() 在超时到期时抛出SocketTimeoutException。在Socket 上设置超时会导致读取方法在超时到期时抛出SocketTimeoutException。没有像read_timeout 这样的代码,所以我不知道为什么它被格式化为代码。他已经说过“服务器断开连接很好”。客户端根本不一定会检测到服务器关闭:请参阅我的答案。
  • @EJP:我误用了嵌入此链接的快捷方式来“读取超时”:developer.android.com/reference/java/net/…
  • @KelvinTrinh 另一方面,OP 没有说明调用ServerSocket.setSoTimeout(). Socket.setSoTImeout() 可以在服务器或客户端调用,在两种情况下都具有相同的效果:它设置读取超时;这就是OP所说的他所做的。客户端不需要在十秒内完成整个文件的发送:每次调用read() 都会重新开始读取超时。您的链接中没有任何内容与我在这里所说的相矛盾。
猜你喜欢
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多