我知道TIME_WAIT是TCP/IP的一个重要部分,但在SO(和其他地方)上有很多关于每秒创建多个套接字并且服务器最终用尽短暂端口的问题。
我发现当使用TCPClient
(或者Socket
)时,如果调用Close()
或Dispose()
方法,套接字的TCP状态会变为TIME_WAIT,并且会在完全关闭之前遵守超时期间。
然而,如果我只是将变量设置为null
,则套接字将在下一次GC运行时完全关闭,而不需要经历TIME_WAIT状态。
对我来说,这没有太多意义,因为这是一个IDisposable
对象,难道GC不应该也调用Dispose()
方法吗?
这里有一些PowerShell代码可以演示(此机器未安装VS)。 我使用Sysinternals的TCPView实时检查套接字状态:
$sockets = @()
0..100 | % {
$sockets += New-Object System.Net.Sockets.TcpClient
$sockets[$_].Connect('localhost', 80)
}
Start-Sleep -Seconds 10
$sockets = $null
[GC]::Collect()
使用这种方法,套接字永远不会进入TIME_WAIT状态。如果我在手动调用Close()
或Dispose()
之前只是关闭应用程序也是如此。有人能否解释一下,这是否是一个好的做法(我想象人们会说不是)。
编辑
GC已经回答了该问题的相关部分,但我仍然对为什么这会对套接字状态产生任何影响以及这是否应该由操作系统而不是.NET控制感兴趣。
还有,我想知道使用这种方法来防止TIME_WAIT状态是否是一个好的做法,最终是否存在某个bug(即,所有套接字是否都应该经过TIME_WAIT状态?)
Dispose()
方法?” GC永远不会调用Dispose()
。它只会调用类的终结器(如果有的话)。通常,Dispose()
从终结器中调用,但需要重申的是:Dispose
仅适用于使用(using)场景。GC完全不关心它。 - Jeroen MostertGC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
来强制执行。您的GC.Collect();
调用只是将套接字放入终结器队列(如果有的话)- 套接字终结器可能需要很长时间(实际上经常会导致进程崩溃)。套接字最可能不处于 TIME_WAIT 状态,是因为它们甚至还没有被关闭。当您杀死进程时也是如此 - 即使对于操作系统来说,关闭 TCP 套接字也是一项非常棘手的任务。列出所有端口,而不仅仅是 TIME_WAIT。 - LuaanClose()
或Dispose()
关闭它们,那么它们就会从已建立状态变为TIME_WAIT状态。所以,虽然你可能是对的,我的代码可能缺少某些东西,但它们肯定不会因为尚未关闭而不进入TIME_WAIT状态。 - cogumel0