4

我读过一个 Tcp Echo 服务器的例子,有些事情我不清楚。

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() 写入的数据是立即发送到客户端(甚至可能仍在发送),还是仅在已经写入流(由客户端)处理的数据之后发送。

下面的问题甚至可以澄清前面的问题:如果客户端通过写入流来发送一些数据,这些数据是否移动到服务器端的消息队列中等待读取,因此流实际上是“空的”?这可以解释为什么服务器可以立即写入流 - 因为来自流的数据实际上在其他地方缓冲......?

4

3 回答 3

4

TCP 连接原则上是全双工的。所以你正在处理 2 个独立的频道,是的,双方可以同时写作。

于 2009-10-23T07:18:16.003 回答
2

提示:在该示例中,方法调用 NetworkStream.Read 是阻塞的。

这本书是绝对正确的——对 TCP 流的原始访问并不意味着任何额外的“分块”,例如,在这个例子中,一次可以很容易地处理一个字节。但是,批量执行读取和写入(通常使用暴露的缓冲区)可以提高处理效率(通常是由于系统调用较少)。网络层和网络硬件也采用了自己的缓冲区形式。

实际上并不能保证从 Write() 写入的数据实际上会在更多 Reads() 成功完成之前被写入:即使数据在一层中刷新,也不意味着它在另一层中刷新,并且绝对不能保证数据已经回到客户端。这就是高级协议发挥作用的地方。

通过这个回显示例,数据被尽可能快地推过。写入和读取都将基于底层网络堆栈(特别是发送和接收缓冲区)阻塞,每个堆栈都有自己的一系列缓冲区。

[这当然简化了一些事情——人们总是可以查看 TCP [协议] 本身,它确实将传输特性强加于实际的数据包流。]

于 2009-10-23T06:05:25.010 回答
1

您是对的,从技术上讲,在执行 Read() 操作时,您并没有从线上读取位。您基本上是在读取缓冲数据(由 TCP 接收并按正确顺序排列的块)。发送时,您可以 Flush() 理论上应该立即发送数据,但是现代 TCP 堆栈有一些逻辑,如何以适当大小的数据包收集数据并将它们突发到线路上。

正如 Henk Holterman 解释的那样,TCP 是一个全双工协议(如果所有底层基础设施都支持的话),因此发送和接收数据更多是在服务器/客户端读取和写入数据时。这不像当您服务器发送数据时,客户端会立即读取它。客户端可以发送自己的数据,然后执行 Read(),在这种情况下,数据将在网络缓冲区中停留更长时间,并且可以在一段时间后被丢弃,因为没有人想读取它。至少我在处理我的 supa dupa 服务器/客户端库(-.

于 2009-10-23T08:55:24.653 回答