【发布时间】:2011-02-14 06:45:53
【问题描述】:
据我了解,TcpListener 将在您致电 Start() 后排队连接。每次调用AcceptTcpClient(或BeginAcceptTcpClient)时,它都会从队列中取出一项。
如果我们通过一次向它发送 1,000 个连接来对我们的TcpListener 应用程序进行负载测试,队列的构建速度远远快于我们清除它的速度,导致(最终)客户端超时,因为它没有得到响应,因为它的连接仍在队列中。但是,服务器似乎没有太大压力,我们的应用程序没有消耗太多 CPU 时间,并且机器上的其他受监控资源也没有出汗。感觉我们现在的运行效率不够高。
我们调用BeginAcceptTcpListener,然后立即移交给ThreadPool 线程来实际完成工作,然后再次调用BeginAcceptTcpClient。所涉及的工作似乎没有给机器带来任何压力,它基本上只是 3 秒的睡眠,然后是字典查找,然后将 100 字节写入TcpClient 的流。
这是我们正在使用的TcpListener 代码:
// Thread signal.
private static ManualResetEvent tcpClientConnected = new ManualResetEvent(false);
public void DoBeginAcceptTcpClient(TcpListener listener)
{
// Set the event to nonsignaled state.
tcpClientConnected.Reset();
listener.BeginAcceptTcpClient(
new AsyncCallback(DoAcceptTcpClientCallback),
listener);
// Wait for signal
tcpClientConnected.WaitOne();
}
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
// Get the listener that handles the client request, and the TcpClient
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
if (inProduction)
ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client, serverCertificate)); // With SSL
else
ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client)); // Without SSL
// Signal the calling thread to continue.
tcpClientConnected.Set();
}
public void Start()
{
currentHandledRequests = 0;
tcpListener = new TcpListener(IPAddress.Any, 10000);
try
{
tcpListener.Start();
while (true)
DoBeginAcceptTcpClient(tcpListener);
}
catch (SocketException)
{
// The TcpListener is shutting down, exit gracefully
CheckBuffer();
return;
}
}
我假设答案将与使用 Sockets 而不是 TcpListener 或至少使用 TcpListener.AcceptSocket 有关,但我想知道我们将如何去做?
我们的一个想法是调用AcceptTcpClient 并立即将Enqueue 和TcpClient 调用到多个Queue<TcpClient> 对象之一中。这样,我们可以在单独的线程上轮询这些队列(每个线程一个队列),而不会遇到可能在等待其他 Dequeue 操作时阻塞线程的监视器。然后每个队列线程可以使用ThreadPool.QueueUserWorkItem 在ThreadPool 线程中完成工作,然后继续将其队列中的下一个TcpClient 出队。您会推荐这种方法,还是我们正在使用TcpListener 的问题,并且再多的快速出队也无法解决这个问题?
【问题讨论】:
-
我认为您可能有一个错误的假设。被丢弃的传入连接不会从队列中丢弃。他们从来没有进入队列,因为队列已经满了。您可以选择指定更大的队列长度。这将在一定程度上缓解问题。真正的问题是,以如此快的速度猛击服务器并不是很现实。为处理 1k 个客户端而构建的服务器通常仍需要以计量的速度进行连接。
-
用更大的“积压”值试试这个:msdn.microsoft.com/en-us/library/5kh8wf6s(v=VS.100).aspx
-
MSDN 说如果超出队列大小,TcpListener 将抛出 SocketException,我没有看到任何 SocketExceptions(CheckBuffer 方法日志),所以我假设我还没有达到它的大小限制.但这是 1k 连接的好点。
-
澄清一下,这些 SocketExceptions 是在客户端而不是服务器上抛出的。
标签: c# .net sockets tcpclient tcplistener