java Nettysocket。shutdownOutput()等效于避免TCP RST?
我想知道是否有一种方法可以避免在Netty中关闭连接时设置TCP RST标志而不是TCP FIN标志,因为在Netty中TCP接收缓冲区中还有输入数据
用例是:
- 客户端(用C编写)发送包含许多字段的数据包李>
- 服务器读取数据包,在早期字段中遇到错误,引发异常李>
- 异常处理程序捕获异常,写入错误消息,并将写时关闭回调添加到写未来李>
问题是:
接收缓冲区中的剩余数据导致Linux(或Java..)使用RST标志标记TCP数据包。这会阻止客户端读取数据,因为当它开始尝试时,会发现由于socket关闭而导致读取错误
对于直接的Javasocket,我相信解决方案是调用socket。关闭前关闭输出()。在Netty中有没有一个等价的函数
如果我只是继续从socket读取数据,可能不足以避免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的答案。在Netty 4+中可能会提供与shutdownOutput()等效的消息
解决方案是从客户端读取所有数据(但您永远无法确定客户端何时会完全停止发送,尤其是在恶意客户端的情况下),或者只是在发送响应后关闭连接之前等待(请参阅来自Unreputable的回答)
# 1 楼答案
如果您可以从Netty获得底层的
SocketChannel
,而我对这方面不是专家,那么您可以调用channel.socket().shutdownOutput().
我不明白。TCP保证客户端在获得FIN之前将接收其套接字接收缓冲区中的所有数据。如果您谈论的是服务器的套接字接收缓冲区,它将被close()丢弃,客户端进一步尝试发送将得到一个RST,它将成为一个
IOException:
连接重置,因为没有与之关联的连接,因此无处放置它。注意,这一切都是由TCP完成的,而不是Java但在我看来,如果频道不好,你应该在关闭频道之前阅读整个请求
您还可以尝试增加套接字接收缓冲区,使其足够大以容纳整个请求。这样可以确保当您想要关闭连接时,客户端不会仍在发送EDIT:我看到请求是兆字节,所以这不起作用
# 2 楼答案
您可以尝试以下操作:在服务器写入错误消息后,等待500毫秒,然后关闭()。查看客户端现在是否可以接收错误消息
我猜由于TCP延迟确认,服务器接收缓冲区中的数据包没有被确认。如果现在调用close(),则这些数据包的正确响应是RST。但如果调用了shutdownOutput(),这是一个优雅的关闭过程;首先确认数据包
编辑:了解更多信息后的另一次尝试:
应用程序协议是,服务器可以随时响应,即使客户端请求仍在流传输。因此,假设采用阻塞模式,客户端应该有一个从服务器读取的独立线程。一旦客户机从服务器读取响应,它就需要进入写入线程,以停止对服务器的进一步写入。这可以通过简单地关闭()套接字来完成
在服务器端,如果在读取所有请求数据之前写入响应,然后调用close(),则很可能会将RST发送到客户端。显然,如果在接收缓冲区不为空时调用close(),大多数TCP堆栈都会将RST发送到另一端。即使TCP堆栈不这样做,也很可能会在close()之后立即到达更多数据,从而触发RST
当这种情况发生时,客户端很可能无法读取服务器响应,因此出现问题
因此,服务器无法在响应后立即关闭(),它需要等待客户端收到响应。服务器如何知道这一点
首先,客户如何知道它已经收到了完整的响应?也就是说,响应是如何终止的?如果响应由TCP FIN终止,则服务器必须在响应后通过调用shutdownOutput()发送FIN。如果响应是自终止的,例如通过HTTP内容长度头,则服务器不需要调用shutdownOutput()
根据协议,客户端收到完整响应后,应立即停止向服务器发送更多数据。这是通过粗略地切断连接来实现的;协议没有设计出更优雅的方式。鱼翅或鱼翅都可以
因此,服务器在写入响应后,应该继续从客户端读取,直到EOF或出错。然后它可以关闭套接字
但是,此步骤应该有一个超时,以说明恶意/损坏的客户端和网络问题。在大多数情况下,几秒钟应足以完成该步骤
此外,服务器可能不想从客户端读取,因为它不是免费的。服务器只需等待超过超时时间,然后关闭()