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
,
直接用NetworkStream
写myNetworkStream.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
/EndConnect
、BeginReceive
/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游记