TcpClient/NetworkStream 在同一个实例上同时读写

Posted

技术标签:

【中文标题】TcpClient/NetworkStream 在同一个实例上同时读写【英文标题】:TcpClient/NetworkStream read write simoultanously on same instance 【发布时间】:2020-12-17 09:52:26 【问题描述】:

我需要与只接受 1 个 TCP 连接的硬件进行通信。但是我找不到任何好的例子,其中相同的NetworkStream 用于同时读写。

MSDN documentation 表示如下:

读写操作可以同时在一个 NetworkStream 类的实例,而不需要 同步。只要有一个唯一的线程用于写入 操作和一个用于读取操作的唯一线程,将 读写线程之间没有交叉干扰并且没有 需要同步。

但是,我找不到一个示例来说明如何在不同线程中使用 NetworkStream 的同一实例进行读写。

我不确定我是否应该:

直接使用NetworkStream阅读myNetworkStream.ReadAsync(),或使用StreamReader, 直接用NetworkStreammyNetworkStream.WriteAsync(),或者用StreamWriter, 使用while(true) 直接启动一个新线程,以便在TcpClient 实例化时读取, 保持对我的NetworkStream 实例的全局引用,以便我可以随时编写...

在 TCP 连接/网络通信方面,我远非专家,所以肯定有一些事情我不完全理解......

提前致谢:)

【问题讨论】:

这些都是您可能会或可能不会选择做的事情。这并不是真正的“一种正确的方法”。 你能举个例子说明如何使用同一个NetworStream同时读写吗? 【参考方案1】:

记住 I/O 绑定任务,there is no thread。

您发布的段落并没有真正处理任务,例如:

var client = new TcpClient();
await client.ConnectAsync("example.com", 80);
var stream = client.GetStream();

// start reading from the socket
var rBuf = new byte[2048];
var tRecv = stream.ReadAsync(rBuf, 0, rBuf.Length);

// send data and wait for it to complete
var sBuf = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
await stream.WriteAsync(sBuf, 0, sBuf.Length);

// now await for the data
var bytesRecv = await tRecv;

Console.WriteLine($"Received bytesRecv bytes");

在处理底层流时,不会有两个同时线程同时做某事。

该段落所谈论的内容是这样的:

var client = new TcpClient();
await client.ConnectAsync("example.com", 80);
var stream = client.GetStream();

new Thread(() =>

    var rBuf = new byte[2048];
    var bytesRecv = stream.Read(rBuf, 0, rBuf.Length);
    Console.WriteLine($"Received bytesRecv bytes");
).Start();

Thread.Sleep(500); // ensure the above thread has started

new Thread(() =>

    var sBuf = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
    stream.Write(sBuf, 0, sBuf.Length);
).Start();

Console.ReadKey(true);

在上面的示例中,您有两个不同的线程同时对底层流执行某些操作,并且没有“崩溃”。

至于你应该使用什么模式......你有几种可能性。您有上面的第一个任务示例(现代方法),或者您可以使用阻塞调用(“老派”方法)执行线程化的while(true) 方法。或者Begin...End... 方法(介于老式和现代之间)。

什么是最好的?你选。我个人喜欢在数据可用时触发事件,所以我倾向于使用BeginConnect/EndConnectBeginReceive/EndReceive 等方法。

但有时,我喜欢使用ReadAsync() 执行while(true),但使用CancellationToken 来终止循环。

我确实建议将while(true) 与阻塞调用一起使用。有了 TPL 为我们提供的出色工具,我们不必再启动通用线程来完成任何事情。

【讨论】:

听起来不错,我完全赞成不使用线程,但是,我需要与之通信的硬件可能正在连续发送数据,我怎样才能确保我有某种“进程”,它在收到数据时不断接收和引发事件,同时仍然能够不时地向它发送一些东西,所有这些都不需要在不同的线程中使用阻塞 while(true)? @Seb - 你可以启动一个位于while(true) ReceiveAsync() 中的Task,并在数据准备好时触发一个事件。或者使用BeginReceive 方法,基本上做同样的事情。 一个需要一段时间的任务(真的)?不是反过来吗?任务中的一段时间(真)?

以上是关于TcpClient/NetworkStream 在同一个实例上同时读写的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据