Necesito comunicarme con una pieza de hardware que solo acepta 1 conexión TCP. Pero no puedo encontrar ningún buen ejemplo en el que se utilice el mismo NetworkStream para leer y escribir simultáneamente.

La documentación de MSDN dice lo siguiente:

Las operaciones de lectura y escritura se pueden realizar simultáneamente en una instancia de la clase NetworkStream sin necesidad de sincronización. Siempre que haya un hilo único para las operaciones de escritura y un hilo único para las operaciones de lectura, no habrá interferencia cruzada entre los hilos de lectura y escritura y no se requiere sincronización.

Sin embargo, no puedo encontrar un ejemplo que muestre cómo usar la misma instancia de un NetworkStream en diferentes subprocesos para leer y escribir.

No estoy seguro de si debería:

  • simplemente use el NetworkStream directamente para leer con myNetworkStream.ReadAsync(), o use un StreamReader,
  • simplemente use el NetworkStream directamente para escribir con myNetworkStream.WriteAsync(), o use un StreamWriter,
  • iniciar directamente un nuevo hilo con un while(true) para leer al crear una instancia del TcpClient,
  • mantener una referencia global a mi instancia NetworkStream para que pueda escribir cuando ...

Estoy lejos de ser un experto cuando se trata de conexiones TCP / comunicación de red, por lo que definitivamente hay cosas que no entiendo completamente ...

Gracias de antemano :)

1
Seb 28 ago. 2020 a las 15:53

2 respuestas

La mejor respuesta

Recuerde que con las tareas vinculadas de E / S, no hay hilo .

El párrafo que publicó no trata realmente de tareas, por ejemplo:

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");

No hay dos subprocesos simultáneos que hagan algo al mismo tiempo cuando se trata de la transmisión subyacente.

De lo que habla el párrafo es de hacer algo como esto:

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);

En el ejemplo anterior, tiene dos subprocesos distintos que hacen algo al mismo tiempo con la secuencia subyacente y nada "explota".

En cuanto a qué patrón debes usar ... Tienes algunas posibilidades. Tiene el primer ejemplo anterior con tareas (enfoque moderno), o puede usar el método while(true) enhebrado usando llamadas de bloqueo (enfoque "de la vieja escuela"). O los métodos Begin... y End... (entre la vieja escuela y la moderna).

¿Qué es lo mejor? Tu eliges. Personalmente, me gusta que los eventos se activen cuando hay datos disponibles, por lo que suelo usar los métodos BeginConnect / EndConnect, BeginReceive / EndReceive, etc.

A veces, sin embargo, me gusta hacer un while(true) usando ReadAsync() pero usando un CancellationToken para cerrar el ciclo.

No sugiero usar while(true) con el bloqueo de llamadas. Con las maravillosas herramientas que nos brinda el TPL, ya no deberíamos tener que iniciar subprocesos genéricos para lograr nada.

2
Andy 28 ago. 2020 a las 16:13

Usaría el patrón Pipeline, eche un vistazo a este código de Gitub y el artículo de David Cazador

0
Walter Vehoeven 14 mar. 2021 a las 12:29