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

时间:2012-10-31 22:46:52

标签: java sockets tcp netty

我想知道是否有办法避免在Netty中关闭TCP连接缓冲区中仍有输入数据的连接时设置TCP RST标志而不是TCP FIN标志。

用例是:

  • 客户端(用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 +中找到。

解决方案要么是从客户端读取所有数据(但是您永远无法确定客户端何时完全停止发送,特别是在恶意客户端的情况下),或者只是在发送响应后关闭连接之前等待(见无可争议的回答)。

2 个答案:

答案 0 :(得分:1)

你能试试这个:服务器写错误信息后,等待500ms,然后关闭()。查看客户端是否可以立即收到错误消息。

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


编辑:在了解了有关此事的更多信息后再次尝试:

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

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

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

因此服务器在响应后无法立即关闭(),需要等到客户端收到响应。服务器如何知道?

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

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

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

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

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

答案 1 :(得分:1)

如果您可以从Netty获取基础SocketChannel,我不是专家,您可以致电channel.socket().shutdownOutput().

  

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

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

但在我看来,如果关闭频道,你应该在关闭频道之前阅读整个请求

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