在 Windows Server 2008 R2 上使用异步套接字会导致 CPU 使用率达到 100%
我有一个相当通用的 C# 套接字服务器,它使用套接字类的异步方法 - BeginAccept()、BeginReceive() 等。过去 4 年里,该服务器在许多运行 Win Server 2003 的客户站点上运行良好。最近,我将其安装在 64 位 Windows Server 2008 R2 服务器上。一切看起来都很好,直到第一个客户端连接并在接受处理程序中发出 BeginReceive() 和 BeginAccept() 调用。发生这种情况时,CPU 使用率会飙升至 100%,并保持这种状态,直到我关闭侦听套接字。
不确定这是否重要,但服务器正在虚拟机中运行。
做了很多测试,但似乎没有任何帮助。使用 Process Explorer,我可以看到两个线程在 BeginReceive()/BeginAccept() 调用后不久启动,并且它们是消耗处理器的线程。不幸的是,我无法在我的 Win7 64 位工作站上重现此问题。
我做了很多研究,到目前为止我发现的是以下两篇知识库文章,它们暗示 Server 2008 R2 的 TCP/IP 组件可能存在问题,但它们仅作为修补程序提供:KB2465772 和KB2477730。在我更确定他们会解决问题之前,我不愿意让我的客户安装它们。
还有其他人遇到过这个问题吗?如果是这样,您需要做什么来解决这个问题?
这是我认为导致这种情况的方法:
private void AcceptCallback(IAsyncResult result) {
ConnectionInfo connection = new ConnectionInfo();
try {
// Finish accept.
Socket listener = (Socket)result.AsyncState;
connection.Socket = listener.EndAccept(result);
connection.Request = new StringBuilder(256);
// Start receive and a new accept.
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), listener);
// CPU usage spikes at 100% shortly after this...
}
catch (ObjectDisposedException /*ode*/) {
_log.Debug("[AcceptCallback] ObjectDisposedException");
}
catch (SocketException se) {
connection.Socket.Close();
_log.ErrorFormat("[AcceptCallback] Socket Exception ({0}: {1} {2}", connection.ClientAddress, se.ErrorCode, se.Message);
}
catch (Exception ex) {
connection.Socket.Close();
_log.ErrorFormat("[AcceptCallback] Exception {0}: {1}", connection.ClientAddress, ex.Message);
}
}
I have a fairly generic C# socket server that uses the asynchronous methods of the socket classes - BeginAccept(), BeginReceive(), etc. This server has been working great for the last 4 years at many customer sites running Win Server 2003. Recently I installed it on a Windows Server 2008 R2 server, 64-bit. Everything looks fine until the first client connects and issues a BeginReceive() and a BeginAccept() call in the accept handler. When this happens, the CPU usage spikes to 100% and stays that way until I close the listening socket.
Not sure that it matters, but the server is running in a virtual machine.
Have done a lot of testing, but nothing seems to help. Using Process Explorer, I can see that two threads are spun up shortly after the BeginReceive()/BeginAccept() calls, and they are the ones that are consuming the processor. Unfortunately, I am not able to reproduce this problem on my Win7 64-bit workstation.
I have done a lot of research, and all that I have found so far is the following two KB articles that imply that Server 2008 R2 may have an issue with the TCP/IP components, but they are only available as hot fixes: KB2465772 and KB2477730. Am reluctant to have my customer install them until I am more certain that they will fix the issue.
Has anyone else had this problem? If so, what did you have to do to resolve this issue?
Here is the method that I believe causes the situation:
private void AcceptCallback(IAsyncResult result) {
ConnectionInfo connection = new ConnectionInfo();
try {
// Finish accept.
Socket listener = (Socket)result.AsyncState;
connection.Socket = listener.EndAccept(result);
connection.Request = new StringBuilder(256);
// Start receive and a new accept.
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), listener);
// CPU usage spikes at 100% shortly after this...
}
catch (ObjectDisposedException /*ode*/) {
_log.Debug("[AcceptCallback] ObjectDisposedException");
}
catch (SocketException se) {
connection.Socket.Close();
_log.ErrorFormat("[AcceptCallback] Socket Exception ({0}: {1} {2}", connection.ClientAddress, se.ErrorCode, se.Message);
}
catch (Exception ex) {
connection.Socket.Close();
_log.ErrorFormat("[AcceptCallback] Exception {0}: {1}", connection.ClientAddress, ex.Message);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
该问题是由于在设置侦听器套接字时多次调用 BeginAccept() 造成的。不知道为什么问题只发生在 64 位服务器上,但更改如下所示的代码解决了问题。
原始代码:
如下:
The issue was caused by having more than one call to BeginAccept() when setting up the listener socket. Do not know why the problem only occurs on 64bit servers, but changing the code as shown below fixed the issue.
Original code:
To the following:
我知道您在“Wire Performance”文章中找到了 SetupServerSocket() - 如果您有快速的新客户端连接,原始的 10x for 循环将支持 10 个侦听线程。您已将其更改为一名侦听器。如果 Win2k8r2 有这样的错误,也许这是唯一可能的解决方案。您可能希望确保客户端中有可靠的连接重试代码。
使用 .NET 中的高性能套接字更接近线路
http://msdn.microsoft.com/en-us/magazine/cc300760.aspx
I know the "Wire Performance" article where you found SetupServerSocket() - the original 10x for loop is to support 10 listen threads if you have rapid new client connections. You've changed it to one listener. Maybe that's the only possible solution if Win2k8r2 has such a bug. You might want to be sure you have robust connect retry code in your client.
Get Closer to the Wire with High-Performance Sockets in .NET
http://msdn.microsoft.com/en-us/magazine/cc300760.aspx