Как работает NetworkStream в двух направлениях? - PullRequest
2 голосов
/ 23 октября 2009

Я прочитал пример 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 (), отправленными немедленно клиенту (который, возможно, все еще отправляет), или только после обработки данных, уже записанных в поток (клиентом).

Следующий вопрос может даже прояснить предыдущий: если клиент отправляет некоторые данные посредством записи в поток, перемещаются ли эти данные в очередь сообщений на стороне сервера, ожидая чтения, поэтому поток фактически «пуст»? Это объясняет, почему сервер может немедленно записывать в поток - потому что данные, поступающие из потока, фактически буферизуются в другом месте ...?

Ответы [ 3 ]

2 голосов
/ 23 октября 2009

TCP-соединение в принципе дуплексное. Таким образом, вы имеете дело с двумя отдельными каналами, и да, обе стороны могут писать одновременно.

2 голосов
/ 23 октября 2009

Подсказка: в этом примере вызов метода NetworkStream.Read блокируется.

Книга абсолютно верна - необработанный доступ к потокам TCP не подразумевает какого-либо дополнительного «чанкинга», и, например, в этом примере, один байт может быть легко обработан за один раз. Однако выполнение чтения и записи в пакетном режиме (обычно с открытыми буферами) может обеспечить более эффективную обработку (часто в результате меньшего количества системных вызовов). Сетевой уровень и сетевое оборудование также используют свои собственные буферы.

На самом деле нет никакой гарантии, что данные, записанные с помощью Write (), будут действительно записаны до успешного завершения операции Reads (): даже если данные сбрасываются в одном слое, это не означает, что они сбрасываются в другом, и нет абсолютно никаких гарантий что данные вернулись к клиенту. Здесь вступают в действие протоколы более высокого уровня.

В этом примере с эхом данные просто передаются настолько быстро, насколько это возможно. И запись, и чтение будут блокироваться на основе базового сетевого стека (в частности, буферов отправки и получения), каждый из которых имеет свою собственную серию буферов.

[Это, конечно, немного упрощает - всегда можно взглянуть на сам TCP [протокол], который накладывает характеристики передачи на фактический поток пакетов.]

1 голос
/ 23 октября 2009

Вы правы, что технически при выполнении операции чтения () вы не считываете биты с провода. Вы в основном читаете буферизованные данные (фрагменты, полученные по TCP и расположенные в правильном порядке). При отправке вы можете использовать Flush (), который теоретически должен отправлять данные немедленно, но в современных TCP-стеках есть немного логики, как собирать данные в пакеты соответствующего размера и передавать их в провод.

Как объяснил Хенк Холтерман, TCP - это дуплексный протокол (если поддерживается всей базовой инфраструктурой), поэтому отправка и получение данных - это больше, когда сервер / клиент читает и записывает данные. Это не так, когда вы отправляете данные на сервер, клиент сразу их прочитает. Клиент может отправлять свои собственные данные и затем выполнять чтение (), в этом случае данные будут оставаться в сетевом буфере дольше и могут быть отброшены через некоторое время, если никто не захочет их прочитать. По крайней мере, я испытал это, когда имел дело с моей библиотекой сервера / клиента supa dupa (-.

...