【问题标题】:How does NetworkStream work in two directions?NetworkStream 如何在两个方向上工作?
【发布时间】:2010-12-09 08:31:16
【问题描述】:

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

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

【问题讨论】:

    标签: c# packet tcplistener netstream


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 来自同一本书:“为什么不只是单次读取?TCP 不保留 Read() 和 Write() 消息边界。也就是说,即使我们使用单次 Write( ),回显服务器可能会分多个块接收它。即使回显字符串由回显服务器以一个块处理,回复仍然可能被 TCP 分解成碎片。初学者最常见的错误之一是假设单个 Write() 发送的数据将始终由单个 Read() 接收。”所以,我想这不会改变问题。如果有,请简要说明它是如何变化的。
    • 这里的问题可能是,如果一端连续发送两条消息并且您没有机制(标记,消息长度指示器)您可以一次读取两条消息(两条短消息适合一个读取缓冲区)或在同一循环期间(第一个循环读取第一条消息的一部分,第二个循环 - 第一条消息的结尾和第二条消息的开始,依此类推)。理解这一点的最简单方法是想象您读取的所有数据就像所有收到的消息混合在一起的流。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-18
        • 2018-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多