#c# #.net-core #tcpclient
#c# #.net-ядро #tcpclient
Вопрос:
Мне нужно связаться с аппаратным обеспечением, которое принимает только 1 TCP-соединение. Но я не могу найти ни одного хорошего примера, где то же самое NetworkStream
используется для одновременного чтения и записи.
В документации MSDN говорится следующее:
Операции чтения и записи могут выполняться одновременно в экземпляре класса NetworkStream без необходимости синхронизации. Пока существует один уникальный поток для операций записи и один уникальный поток для операций чтения, перекрестных помех между потоками чтения и записи не будет, и синхронизация не требуется.
Однако я не могу найти пример, показывающий, как использовать один и тот же экземпляр NetworkStream
в разных потоках для чтения и записи.
Я не уверен, следует ли мне:
- просто используйте
NetworkStream
непосредственно для чтения с помощьюmyNetworkStream.ReadAsync()
или используйтеStreamReader
, - просто используйте
NetworkStream
непосредственно для записи сmyNetworkStream.WriteAsync()
или используйтеStreamWriter
, - непосредственно запускает новый поток с
while(true)
для чтения при создании экземпляраTcpClient
, - сохраняйте глобальную ссылку на мой
NetworkStream
экземпляр, чтобы я мог писать в любое время…
Я далек от того, чтобы быть экспертом, когда дело доходит до TCP-соединений / сетевого взаимодействия, поэтому определенно есть вещи, которые я не до конца понимаю…
Заранее спасибо 🙂
Комментарии:
1. Это все, что вы можете делать, а можете и не делать. На самом деле это не «один правильный способ сделать это». Ситуация такого рода.
2. Не могли бы вы привести пример того, как использовать один и тот же сетевой поток для одновременного чтения и записи?
Ответ №1:
Помните, что с задачами, связанными с вводом-выводом, нет потока.
Опубликованный вами абзац на самом деле не касается задач, например:
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.1rnHost: example.comrnrn");
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.1rnHost: example.comrnrn");
stream.Write(sBuf, 0, sBuf.Length);
}).Start();
Console.ReadKey(true);
В приведенном выше примере у вас есть два разных потока, которые одновременно что-то делают с базовым потоком, и ничего не «взрывается».
Что касается того, какой шаблон вы должны использовать… У вас есть несколько возможностей. У вас есть первый пример выше с задачами (современный подход), или вы могли бы использовать поточный while(true)
метод, используя блокирующие вызовы (подход «старой школы»). Или методы Begin...
и End...
(между старой школой и современными).
Что лучше? Вы выбираете. Лично мне нравится, когда события запускаются при наличии данных, поэтому я склонен использовать методы BeginConnect
/ EndConnect
, BeginReceive
/ EndReceive
и т.д.
Иногда, однако, мне нравится делать while(true)
использование ReadAsync()
, но использовать CancellationToken
для прерывания цикла.
Я не предлагаю использовать while(true)
с блокировкой вызовов. Имея замечательные инструменты, которые предоставляет нам TPL, нам больше не нужно запускать общие потоки для выполнения чего-либо.
Комментарии:
1. Звучит неплохо, я полностью за то, чтобы не использовать потоки, однако аппаратное обеспечение, с которым мне нужно взаимодействовать, может непрерывно отправлять данные, как я могу гарантировать, что у меня есть какой-то «процесс», который продолжает получать и вызывать события всякий раз, когда данные получены, при этом все еще имея возможность время от времени отправлять на него что-то, и все это без использования блокировки while (true) в другом потоке?
2. @Seb — вы могли бы запустить
Task
который находится вwhile(true) { ReceiveAsync() }
и просто запустить событие, когда данные будут готовы. Или используйтеBeginReceive
метод, который в основном делает то же самое.3. Задача, которая выполняется некоторое время (верно)? А не наоборот? Некоторое время (истина) в задаче?