【问题标题】:Simple rule for closing a TCP protocol关闭 TCP 协议的简单规则
【发布时间】:2021-02-13 02:16:29
【问题描述】:

我正在使用 Java 设计一个系统,其中许多服务器彼此打开 TCP 连接以发送相对少量的数据(比如小于 5KB)。在正常负载下一切正常。但是当我对系统施加压力时,我的一些协议会抛出异常。异常的原因是当对等体 A 完成写入数据时,它在 OutputStream 上调用 .flush() 并关闭套接字。对等体 B 的操作系统在 B 有机会读完所有数据之前终止连接,导致异常。

我怀疑对系统施加压力会加剧这个问题,因为它减少了 B 的线程从套接字读取数据的可用时间,但我不确定。

我看到了通过让客户端启动套接字关闭来解决此问题的建议。 但是,我不清楚谁是客户。在协议期间,A 和 B 都可以相互发送多条消息。

所以我现在想知道是否有人可以推荐一个简单的规则,即哪一方应该关闭连接,以及如何关闭。根据我阅读的内容,我认为读取协议最后一条消息的一方应该关闭它。关于如何关闭连接,假设对等体 B 收到最后一条消息。所以一个简单的解决方案是让 B 向 A 发送一个字节,然后关闭套接字。 A 等待这个字节,然后关闭套接字。

有什么想法吗?

【问题讨论】:

  • B 出现什么错误?你能发布一些代码吗?当 A 关闭套接字时,B 不应出现异常。它应该仍然能够在到达 EOF 之前读取所有待处理的数据。操作系统会将未读数据存储在缓冲区中;它不会把它扔掉。如果您可以创建一个minimal reproducible example,其中包含演示问题的代码和完整的错误消息,将会很有帮助。
  • 上次我运行它时我得到了这个(我知道这是一个写异常,这与我上面描述的不同(它是一个读异常)。但它是同一件事。出于某种原因套接字在另一侧有机会完成写入之前被一侧关闭) java.net.SocketException: Broken pipe java.net.SocketOutputStream.socketWrite0(Native Method) java.net.SocketOutputStream.socketWrite(SocketOutputStream.java: 109) java.net.SocketOutputStream.write(SocketOutputStream.java:141)
  • 写异常是可以的。它告诉你对方已经关闭了连接,所以没有必要尝试写任何东西,因为没有人在听。只是不要将其视为致命错误。

标签: java sockets tcp


【解决方案1】:

在做了更多阅读之后,似乎当一个套接字关闭时,有一个 FIN ACK 消息的交换。我认为它被称为优雅关闭(与强制关闭相反)。这些消息并不是真正的消息,而只是数据包中的标志;数据包还可能携带对等方正在写入的数据。接收到数据时,即使设置了这些标志,它也会存储在接收缓冲区中。关键是即使在套接字关闭后也可以从缓冲区中读取数据。但是,在套接字关闭后不能将其写入套接字。如果我错了,请纠正我。

所以,A 写 B 就可以了,让 A 关闭 A 端的套接字,让 B 读取,然后让 B 关闭 B 端的套接字。

关于我的问题中描述的问题,我发现实际发生的事情是这样的: A 启动与 B 的协议。 B 正在做一些耗时过长且受超时保护的前期工作。因为这段代码在socket上的try-with-resources子句中,当超时发生时,线程首先被中断,然后socket被关闭。 B 的代码位于一个包装类中,该类检查协议执行期间是否发生异常。如果是,则向 A 写入错误代码。但由于套接字已关闭,因此写入操作失败。 此时 A 不知道超时并开始向 B 写入数据。所以 A 也出现异常

【讨论】:

    【解决方案2】:

    有一种方法Socket.shutdownInput() 允许一侧关闭一个方向。另一端将获得EOF,然后可以关闭套接字。

    【讨论】:

    • shutdownInput() 不会导致对等端出现 EOF。你想的是shutdownOutput(),但是close()的效果完全一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 2011-09-18
    相关资源
    最近更新 更多