【发布时间】:2015-11-09 14:52:40
【问题描述】:
我正在使用 TcpClient 和 NetworkStream 对象编写服务器/客户端项目。我希望许多客户端连接到服务器,它们存储在自定义NetworkNode 对象的List<> 中,每个对象都有一个TcpClient 和一个NetworkStream 用于与各自的客户端通信。
服务器需要能够保持与客户端的连接,并在收到消息后立即(快速)等待并采取行动。同步轮询对于这个应用程序来说是非常不可取的,我也不想这样编码。
目前服务器正在异步接受客户端并将它们添加到List<>,运行非常顺利。我已经使用一个控制台应用程序对此进行了测试,该应用程序生成多达 100 个客户端并在很短的时间内(
将客户端添加到List<> 时,对象使用GetStream() 方法返回客户端的NetworkStream 对象。我正在尝试使用NetworkStream.BeginRead() 方法来实现来自每个TCP 客户端的异步数据接收。第一次调用方法如下:
this.Stream.BeginRead(readBuffer, readBufferOffset, readBuffer.Length - readBufferOffset,
new AsyncCallback(nodeStreamReadCallback), this.Stream);
因为控制台测试应用程序一连接到服务器就发送一些数据,对象的readCallback(IAsyncResult) 方法几乎立即被调用:
private void readCallback(IAsyncResult ar)
{
NetworkStream _stream = (NetworkStream)ar.AsyncState;
int _bytesRead = 0;
_bytesRead = _stream.EndRead(ar);
this.Stream.Write(readBuffer, readBufferOffset, _bytesRead);
//increase buffer offset value
readBufferOffset += _bytesRead;
//TODO process the received data
...
//wait for the next chunk of data
this.Stream.BeginRead(readBuffer, readBufferOffset, readBuffer.Length - readBufferOffset,
new AsyncCallback(readCallback), this.Stream);
}
我正在对Stream.BeginRead() 进行第二次调用,目的是等待下一个数据块到达或可用,无论将来何时。
当我注释掉对Stream.BeginRead() 的第二次调用时,一切都运行得非常顺利。所有数据都被接收并发送回每个客户端,没有延迟,并且线程使用极少(在此过程中平均需要 2 到 3 个额外线程)。
但是 - 即使只有一个 单个客户端 已连接,如果我尝试在 readCallback() 方法(如上)中对 Stream.BeginRead() 进行 第二次调用我遇到大量争用问题。对于单个客户端,第二次调用后的 CPU 使用率从 ~ 0% 跳到 30% 到 60% 之间,线程数可以从 11 跳到多达 35 个。
所以这是一个线程或递归问题,我觉得我应该等待一些事情,但我不能完全理解这里发生的事情。这与我在 TcpListener.BeginAcceptTcpClient() 中使用的模式相同,所以我认为它必须以不同的方式运行。
感谢您提供的所有建议,并提前感谢您的帮助!
【问题讨论】:
-
你检查你的
_bytesRead是否为0?因为这意味着您的流在远程端已关闭。在这样的流上再次调用BeginRead将直接导致调用您的回调,一次又一次读取字节数为 0。 -
你在哪里重置
readBufferOffset? -
@abto 你是对的!在我的控制台测试应用程序中,我通过
using(NetworkStream stream = client.GetStream(){}块发送数据。当 using 块完成发送数据时,流不再可用,并在_bytesRead参数中返回 0。检查这一点可以防止无限循环。谢谢!确实这是答案,请您将其移至答案中,以便我将其标记为正确。
标签: c# multithreading asynchronous tcpclient networkstream