Netty socket.shutdownOutput() 相当于避免 TCP RST?

Tre*_*man 5 java sockets tcp netty

我想知道在 Netty 中关闭连接时,是否有一种方法可以避免设置 TCP RST 标志而不是 TCP FIN 标志,因为 TCP 接收缓冲区中仍有输入数据。

用例是:

  • 客户端(用C编写)发送包含许多字段的数据包。
  • 服务器读取数据包,在早期字段上遇到错误,引发异常。
  • 异常处理程序捕获异常,写入错误消息,并将写入时关闭回调添加到写入未来。

问题是:

接收缓冲区中的剩余数据会导致 Linux(或 Java..)使用 RST 标志来标记 TCP 数据包。这可以防止客户端读取数据,因为当它尝试尝试时,会发现由于套接字关闭而出现读取错误。

对于直接的Java套接字,我相信解决方案是在关闭之前调用socket.shutdownOutput()。Netty 中是否有等效的函数或解决此问题的方法?

如果我只是继续从套接字读取数据,可能不足以避免 RST,因为调用 close 时缓冲区中可能有也可能没有数据。

供参考: http: //cs.baylor.edu/~donahoo/practical/CSockets/TCPRST.pdf

更新:

问题的另一个参考和描述: http://docs.oracle.com/javase/1.5.0/docs/guide/net/articles/connection_release.html

调用 shutdownOutput() 应该有助于更有序地关闭连接(通过发送 FIN),但如果客户端仍在发送数据,则无论如何都会发送 RST 消息(请参阅 EJP 的答案。 shutdownOutput() 等效项可能可用在 Netty 4+ 中。

解决方案是从客户端读取所有数据(但您永远无法确定客户端何时完全停止发送,尤其是在恶意客户端的情况下),或者只是在发送响应后关闭连接之前等待(请参阅答案来自不名誉的)。

irr*_*ble 0

你可以试试这个:服务器写入错误消息后,等待500ms,然后close()。现在看看客户端是否可以收到错误消息。

我猜测由于 TCP 延迟确认,服务器接收缓冲区中的数据包尚未被确认。如果现在调用 close(),这些数据包的正确响应是 RST。但如果调用 shutdownOutput(),则这是一个优雅的关闭过程;数据包首先被确认。


编辑:了解更多有关此事后的另一次尝试:

应用程序协议是,即使客户端请求仍在传输,服务器也可以随时响应。因此,假设阻塞模式,客户端应该有一个单独的线程从服务器读取数据。一旦客户端从服务器读取响应,它就需要闯入写入线程,以停止进一步写入服务器。这可以通过简单地 close() 套接字来完成。

在服务器端,如果在读取所有请求数据之前写入响应,然后调用 close(),则很可能将 RST 发送到客户端。显然,如果在接收缓冲区不为空时调用 close(),大多数 TCP 堆栈都会将 RST 发送到另一端。即使 TCP 堆栈不这样做,在 close() 之后很可能会有更多数据立即到达,无论如何都会触发 RST。

当发生这种情况时,客户端很可能无法读取服务器响应,从而出现问题。

所以服务端不能在响应后立即close(),它需要等待客户端收到响应。服务器如何知道这一点?

首先,客户端如何知道它已经收到完整的响应?也就是说,响应是如何终止的?如果响应由 TCP FIN 终止,则服务器必须在响应后通过调用 shutdownOutput() 发送 FIN。如果响应是自行终止的,例如通过 HTTP Content-Length 标头,服务器不需要调用 shutdownOutput()。

根据协议,客户端收到完整响应后,应立即停止向服务器发送更多数据。这是通过粗暴地切断连接来完成的;该协议并没有设计出更优雅的方式。FIN 或 RST 都可以。

因此,服务器在写入响应后,应该继续从客户端读取,直到 EOF 或错误。然后它可以 close() 套接字。

但是,此步骤应该有一个超时,以解决恶意/损坏的客户端和网络问题。在大多数情况下,几秒钟就足以完成该步骤。

此外,服务器可能不想从客户端读取数据,因为它不是免费的。服务器可以简单地等待超时,然后 close()。