我想知道是否有办法避免在Netty中关闭TCP连接缓冲区中仍有输入数据的连接时设置TCP RST标志而不是TCP FIN标志。
用例是:
问题是:
接收缓冲区中的剩余数据导致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 +中找到。
解决方案要么是从客户端读取所有数据(但是您永远无法确定客户端何时完全停止发送,特别是在恶意客户端的情况下),或者只是在发送响应后关闭连接之前等待(见无可争议的回答)。
答案 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。
但在我看来,如果关闭频道,你应该在关闭频道之前阅读整个请求。
您还可以尝试增加套接字接收缓冲区,使其足以容纳整个请求。这可以确保在您想要关闭连接时客户端仍然不会发送。 编辑:我看到请求是兆字节,所以这不起作用。