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

问题描述

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

MSDN documentation说:

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

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

我不确定是否应该:

  • 仅将NetworkStream直接用于myNetworkStream.ReadAsync()的阅读,或使用StreamReader
  • 仅将NetworkStream直接用于myNetworkStream.WriteAsync()书写,或使用StreamWriter
  • 直接使用while(true)启动一个新线程,以在实例化TcpClient时进行读取,
  • 保留对NetworkStream实例的全局引用,以便我可以在任何时候编写...

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

预先感谢:)

解决方法

记住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,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,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,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,sBuf.Length);
}).Start();

Console.ReadKey(true);

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

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

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

尽管有时候,我喜欢使用while(true)来进行ReadAsync(),但是使用CancellationToken来杀死循环。

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

,

我会使用流水线模式, 查看 This Gitub 代码和来自 David Fowler

的文章