Как работает NetworkStream в двух направлениях?
-
05-07-2019 - |
Вопрос
Я прочитал пример Tcp Echo Server, и некоторые вещи мне неясны.
TcpClient client = null;
NetworkStream netStream = null;
try {
client = listener.AcceptTcpClient();
netStream = client.GetStream();
int totalBytesEchoed = 0;
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0) {
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
netStream.Close();
client.Close();
} catch {
netStream.Close();
}
Когда сервер получает пакет (цикл while), он считывает данные в rcvBuffer и записывает их в поток.
Что меня смущает, так это хронологический порядок сообщений в общении. Являются ли данные, записанные с помощью netStream.Write (), отправленными немедленно клиенту (который, возможно, все еще отправляет), или только после обработки данных, уже записанных в поток (клиентом).
Следующий вопрос может даже прояснить предыдущий: если клиент отправляет некоторые данные путем записи в поток, это означает, что данные перемещаются в очередь сообщений на стороне сервера, ожидая чтения, поэтому поток фактически "пуст"? Это объясняет, почему сервер может немедленно записывать в поток - потому что данные, поступающие из потока, фактически буферизуются в другом месте ...?
Решение
Совет: в этом примере вызов метода NetworkStream.Read блокируется.
Книга абсолютно правильная - необработанный доступ к потокам TCP не подразумевает каких-либо дополнительных «порций». и, в этом примере, например, один байт может быть легко обработан за один раз. Однако выполнение чтения и записи в пакетном режиме (обычно с открытыми буферами) может обеспечить более эффективную обработку (часто в результате меньшего количества системных вызовов). Сетевой уровень и сетевое оборудование также используют собственные формы буферов.
На самом деле нет никакой гарантии, что данные, записанные с помощью Write (), будут на самом деле записаны до успешного завершения операции Reads (): даже если данные сбрасываются в одном слое, это не означает, что они сбрасываются в другом, и нет абсолютно никаких гарантий что данные вернулись к клиенту. Здесь вступают в действие протоколы более высокого уровня.
В этом примере с эхом данные просто передаются настолько быстро, насколько это возможно. И запись, и чтение будут блокироваться на основе базового сетевого стека (в частности, буферов отправки и получения), каждый из которых имеет свою собственную серию буферов.
[Это, конечно, немного упрощает - всегда можно взглянуть на сам TCP [протокол], который накладывает характеристики передачи на фактический поток пакетов.]
Другие советы
TCP-соединение в принципе дуплексное. Таким образом, вы имеете дело с двумя отдельными каналами, и да, обе стороны могут писать одновременно. Р>
Вы правы, что технически, выполняя операцию Read (), вы не читаете биты по проводам. Вы в основном читаете буферизованные данные (фрагменты, полученные по TCP и расположенные в правильном порядке). При отправке вы можете использовать Flush (), который теоретически должен отправлять данные немедленно, но в современных TCP-стеках есть немного логики, как собирать данные в пакеты соответствующего размера и передавать их на провод.
Как объяснил Хенк Холтерман, TCP является полнодуплексным протоколом (если поддерживается всей базовой инфраструктурой), поэтому отправка и получение данных - это больше, когда ваш сервер / клиент читает и записывает данные. Это не так, когда вы отправляете данные на сервер, клиент сразу их прочитает. Клиент может отправлять свои собственные данные и затем выполнять чтение (), в этом случае данные будут оставаться в сетевом буфере дольше и могут быть отброшены через некоторое время, если никто не захочет их прочитать. По крайней мере, я испытал это, когда имел дело с моей библиотекой сервера / клиента supa dupa (-.