据我了解,TcpListener
一旦您致电,将对连接进行排队Start()
。每次你打电话AcceptTcpClient
(or BeginAcceptTcpClient
),它将从队列中出列一项。
如果我们负载测试我们的TcpListener
通过一次向应用程序发送 1,000 个连接,队列的构建速度远远快于我们清除它的速度,导致(最终)客户端超时,因为它没有得到响应,因为它的连接仍在队列中。然而,服务器似乎并没有承受太大的压力,我们的应用程序没有消耗太多的 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
the TcpClient
成多个之一Queue<TcpClient>
对象。这样,我们就可以在单独的线程上轮询这些队列(每个线程一个队列),而不会遇到可能在等待其他线程时阻塞线程的监视器。Dequeue
运营。然后每个队列线程可以使用ThreadPool.QueueUserWorkItem
完成工作ThreadPool
线程,然后继续使下一个线程出列TcpClient
在其队列中。您会推荐这种方法吗?还是我们正在使用的问题TcpListener
再多的快速出队也无法解决这个问题吗?