TCP 套接字服务器偶尔会随着时间的推移建立 CLOSE_WAITs 直到无法操作
Posted
技术标签:
【中文标题】TCP 套接字服务器偶尔会随着时间的推移建立 CLOSE_WAITs 直到无法操作【英文标题】:TCP Socket Server Builds Up CLOSE_WAITs Occasionally Over Time Until Inoperable 【发布时间】:2010-10-15 08:16:27 【问题描述】:希望有人可以帮助我们,因为我们正在调查可以去!
我们有一个用 C# 编写的简单异步套接字服务器,它接受来自 ASP.NET Web 应用程序的连接,发送一条消息,执行一些处理(通常针对数据库,但也针对其他系统),然后发回响应给客户。客户端负责关闭连接。
如果系统在很长一段时间内(通常是几天)处于高负载下,我们遇到了问题,CLOSE_WAIT 套接字会在服务器盒 (netstat -a) 上堆积到进程无法接受的程度任何进一步的连接。到那时,我们必须重新启动该进程并让它再次运行。
我们已尝试对我们的 ASP.NET 应用程序运行一些负载测试,以尝试重现问题(因为无法从代码中推断出某些问题)。我们认为我们已经解决了这个问题,并最终得到了 WireShark packet trace 的问题,在套接字服务器的日志中表现为 SocketException:
System.Net.Sockets.SocketException:现有连接被远程主机强行关闭 at System.Net.Sockets.Socket.BeginSend(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)
我尝试将数据包跟踪中的问题重现为直接与套接字服务器对话的单线程进程(使用与 ASP.NET 应用程序相同的代码),但我无法做到。
有人对接下来要尝试、检查的事情或我们可能做错的明显事情有任何建议吗?
【问题讨论】:
【参考方案1】:看图
http://en.wikipedia.org/wiki/File:Tcp_state_diagram_fixed.svg
您的客户端通过调用 close() 关闭了连接,这将 FIN 发送到服务器套接字,服务器套接字确认了 FIN 并且其状态现在更改为 CLOSE_WAIT,并保持这种状态,除非服务器发出 close() 调用插座。
您的服务器程序需要检测客户端是否中止了连接,然后立即 close() 以释放端口。如何?请参阅读取()。在读取文件结尾(意味着收到 FIN)时,返回零。
【讨论】:
嗨!我有类似的问题。你介意检查我的代码吗?谢谢! codereview.stackexchange.com/questions/263664/…【参考方案2】:如果您的服务器正在累积CLOSE_WAIT
套接字,则连接完成时它不会关闭其套接字。如果您查看 Chris 帖子评论中的状态图,您会看到一旦套接字关闭并且发送了 FIN
,CLOSE_WAIT
就会转换为 LAST_ACK
。
您说由于异步性质而确定在哪里执行此操作很复杂?这应该不是问题,如果您的 recv 的回调返回 0 字节,您应该关闭套接字(假设一旦您的客户端关闭其一侧的连接,您就无事可做)。如果您确实需要担心继续发送,请在此处执行 Shutdown(recv) 并记下您的客户端已关闭,一旦您完成发送,请执行 Shutdown(send) 和 Close。
您可能会在读取的回调中发出新的读取,返回 0 表示客户端已关闭,这可能会给您带来问题?
【讨论】:
【参考方案3】:客户端负责关闭连接。
客户端和服务器都必须关闭并关闭套接字。客户端没有完成关闭(不太可能 - 因为它会运行终结器)或服务器没有关闭套接字(可能)。
using (Socket s = new Socket(/* */))
/* Do stuff */
s.Shutdown(SocketShutdown.Both);
s.Close();
【讨论】:
在客户端,套接字作为 using(..) 块的一部分被关闭 - 但我们此时不做 .Shutdown 和 .Close 显式 - 这不是正常测试的问题。服务器在我们能找到的所有代码路径中都明确地执行了这两项操作(它很复杂,因为它是异步的)。 @Kieran - 我认为反弹服务器进程清除 CLOSE_WAITs 的事实表明您没有关闭某个地方。【参考方案4】:您不应该只将关闭 TCP 套接字的责任留给客户端。如果客户端进程/机器崩溃会怎样?
理想情况下,您应该设置一个超时,这样如果在一定时间后连接的套接字上没有收到流量,那么它就会被服务器关闭。
【讨论】:
【参考方案5】:无论客户端对套接字的所有操作都完成后发生什么,并且不再需要对套接字进行任何读取操作,客户端都应该发出关闭命令。
这个关闭命令的发出,只是告诉监听器(服务器)连接需要被关闭。
简单来说,当服务器再次发出读取命令时(listener.read() 或 listener.beginread(...) 在异步模式下),读取将返回 0 字节读取,这本身就表明套接字需要由侦听器关闭,因为客户端已停止套接字上的任何其他操作。
【讨论】:
【参考方案6】:CLOSE_WAIT 是为了在套接字关闭后停留一段时间,以防止重复使用相同的套接字号并从旧连接接收数据包。如果您真的快速打开和关闭大量套接字,这只会让您感到悲痛。
编辑 - 应该是 TIME_WAIT,而不是上面的 CLOSE_WAIT。
【讨论】:
如果由于某种原因连接被卡住,他们可以停留更长的时间,请参阅:blog.zhuzhaoyuan.com/2009/03/a-word-on-time_wait-and-close_wait。它不是像 TIME_WAIT 这样的自然现象。 我是不是把 close_wait 和 time_wait 弄糊涂了? 你在考虑 TIME_WAIT 克里斯。 由于问题是关于 CLOSE_WAIT,他的回答是无关紧要的。以上是关于TCP 套接字服务器偶尔会随着时间的推移建立 CLOSE_WAITs 直到无法操作的主要内容,如果未能解决你的问题,请参考以下文章
随着时间的推移,使用 java websockets 实时流式传输模拟视频变得无响应