4

我想知道是否有一种方法可以避免在关闭 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+。

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

4

2 回答 2

1

如果您可以从 Netty 获得底层证券SocketChannel,我不是专家,您可以致电channel.socket().shutdownOutput().

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

我不明白这一点。TCP 保证客户端在获得 FIN 之前将接收到其套接字接收缓冲区中的所有数据。如果您在谈论服务器的套接字接收缓冲区,它将被 close() 丢弃,并且客户端进一步尝试发送将得到一个 RST,这将成为IOException:连接重置',因为没有连接可以与之关联因此无处可放。NB 是 TCP 完成了这一切,而不是 Java。

但是在我看来,如果频道不好,您应该在关闭频道之前阅读整个请求。

您还可以尝试增加套接字接收缓冲区,使其足以容纳整个请求。这确保了当您想要关闭连接时客户端不会仍然发送。编辑:我看到请求是兆字节,所以这不起作用。

于 2012-11-01T00:31:10.987 回答
0

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

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


编辑:进一步了解此事后的另一次尝试:

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

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

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

所以服务器不能在响应后立即关闭(),它需要等到客户端收到响应。服务器怎么知道的?

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

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

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

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

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

于 2012-10-31T23:26:51.737 回答