【发布时间】:2017-11-10 09:33:08
【问题描述】:
我有一个简单的 TCP 服务器,它能够侦听并接受一个端口上的多个连接。然后它不断地等待从其连接中读取数据。为方便起见,它使用称为 ConnectedClient 的 TcpClient 的包装类,并使用 ConnectedClients 的列表(字典)来跟踪所有连接。基本上是这样的:
/* this method waits to accept connections indefinitely until it receives
the signal from the GUI thread to stop. When a connection is accepted, it
adds the connection to the list and calls a method called ProcessClient,
which returns almost immediately.*/
public void waitForConnections() {
// this method has access to a TcpListener called listener that was started elsewhere
try {
while (!_abort) {
TcpClient socketClient = listener.AcceptTcpClient();
//Connected client constructor takes the TcpClient as well as a callback that it uses to print status messages to the GUI if
ConnectedClient client = new ConnectedClient(socketClient, onClientUpdate);
clients.Add(client.id, client);
ProcessClient(client);
}
}
catch (Exception e) {
onStatusUpdate("Exception Occurred: " + e.Message);
}
}
/* This method doesn't do much other than call BeginRead on the connection */
private void ProcessClient(ConnectedClient client) {
try {
// wrapper class contains an internal buffer for extracting data as well as a TcpClient
NetworkStream stream = client.tcpClient.GetStream();
stream.BeginRead(client.buffer, 0, client.tcpClient.ReceiveBufferSize, new AsyncCallback(StreamReadCompleteCallback), client);
}
catch (Exception ex) {
onStatusUpdate(ex.Message);
}
}
在我的回调函数 StreamReadCompleteCallback 中,我调用 EndRead,检查 EndRead 的返回值以检测连接是否已关闭。如果返回值大于零,我提取/处理读取的数据并在同一客户端上再次调用 BeginRead。如果返回值为零,则连接已关闭,我将删除连接(从列表中删除、关闭 TcpClient 等)。
private void StreamReadCompleteCallback(IAsyncResult ar) {
ConnectedClient client = (ConnectedClient)ar.AsyncState;
try {
NetworkStream stream = client.tcpClient.GetStream();
int read = stream.EndRead(ar);
if (read != 0) {
// data extraction/light processing of received data
client.Append(read);
stream.BeginRead(client.buffer, 0, client.tcpClient.ReceiveBufferSize, new AsyncCallback(StreamReadCompleteCallback), client);
}
else {
DisconnectClient(client);
}
}
catch (Exception ex) {
onStatusUpdate(ex.Message);
}
}
所有这些都可以正常工作,我可以接受连接并从多个客户端设备等读取数据。
我的问题是:这种从连接的客户端连续读取的方法会导致每个连接都有一个工作线程正在等待 BeginRead 返回。
所以如果我有 10 个连接,我就有 10 个 BeginReads 进行。
让这么多工作线程坐在那里等待阅读似乎很浪费。还有其他更好的方法来实现这一点吗?如果我有大量活动连接,我最终会耗尽内存来添加连接。
是否有一个线程来轮询每个连接的 DataAvailable 属性,直到出现某些东西,然后让一个线程读取/处理是一种解决方案?
或者创建所有这些工作线程没有我想的那么重要?
【问题讨论】:
标签: c# multithreading tcp server