连续调用 NetworkStream.Write - 有什么不同吗?

Consecutive calls to NetworkStream.Write - does it make a difference?

考虑以下两种发送数据的方法和一种读取数据的方法:

public static void SendConsecutively(this NetworkStream stream)
{
    byte[] header = {1, 2, 3, 4};
    byte[] message = {5, 6, 7, 8, 9, 10};
    stream.Write(header, 0, header.Length);
    stream.Write(message, 0, message.Length);
}

public static void SendAllInOne(this NetworkStream stream)
{
    byte[] headerAndMessage = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    stream.Write(headerAndMessage, 0, headerAndMessage.Length);
}

public static byte[] ReadMessage(this NetworkStream stream)
{
    byte[] data = new byte[10];
    int bytesRead = stream.Read(data, 0, 10);
    return data;        
}

与不拆分数据(如 SendAllInOne 中)相比,是否将数据拆分为两个块(如 SendConsecutively 中)有区别吗?

事实是,在我的测试中,ReadMessage 总是读取 10 个字节。而我正在向其发送 header 和消息(但不知道它是如何实现的)的 3rd-party-server,通常也接收 10 个字节。但有时 - 在极少数情况下 - 他们告诉我 3rd-party-server 只收到 4 个字节。我必须处理这个问题,那个服务器只收到 4 个字节,尽管我确信我已经使用 SendConsecutively 发送了 10 个字节。 (因为我的日志中没有异常,这意味着两个 stream.Write 调用都已发出,这是肯定的。)

因此,一个问题是:两次连续的 stream.Write 调用之间发生了什么?我想:没什么,因为 NetworkStream.Flush 的文档说:"The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no affect on network streams." [1]

我收到了 Wireshark 日志,其中可以看到一个 TCP 数据包仅包含四个 header 字节。每个 stream.Write 调用是否产生一个 TCP 数据包?但话又说回来,我什至需要关心我的数据是如何拆分成 TCP 数据包的吗?因为大消息无论如何都会被分割成多个 TCP 数据包,不是吗? (是的,也有发送的大消息比我在上面的示例中使用的 6 字节大得多 - 例如:4 字节 header + 3000 字节消息)

我的 SendConsecutively 方法是否存在任何缺陷,可能会导致 receiver-side 出现此类问题(同时考虑到 message 可能是 3000 字节,而不是示例中的 6 个字节代码)?

难不成,还是3rd-party-server的问题?如果该服务器将像上面的 ReadMessage 方法一样实现,则可能会出现问题。因为有时(我认为这一定会发生,当消息通过 TCP 分段时),ReadMessage 读取的字节数少于已发送的字节数,并且 return bytesRead 中的数字小于实际消息的长度.消息的其余部分稍后在流中可用(几毫秒?)。对此有什么想法吗?

[1] MSDN,NetworkStream.Flush,https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.write(v=vs.110).aspx

网络流是一种抽象——它隐藏了实现细节。通常开发人员可以忽略发送数据的传输方式——多个数据包或单个数据包,使用哪种协议等。但是,重要的是(至少对我来说,可能对每个使用网络流的人来说)记住一些关于 I/O:

的事实
  • 通常保证数据的顺序(它按照写入的顺序接收)
  • 它可以随时中断(通过抛出异常)
  • 同步读可能会无限期阻塞(大多数情况下不是,但有机会)
  • I/O比其他指令慢很多
  • 数据块的大小在流的不同端是不同的
  • 频繁写入小块的效率可能低于写入一个大块的效率(由于底层传输层导致的额外负载)
  • 读取数据比写入数据慢可能会溢出内部缓冲区

这些事实中的大部分并不取决于电线两端使用的技术。它可以在一侧是 Java 而在另一侧是 .NET - 通常它并不重要,只要通道只是一个字节流。

考虑到这一点,回答您的问题:

Does it make a difference whether or not to split the data into two chunks (like in SendConsecutively) compared to not splitting the data (like in SendAllInOne)? ... What happens between two consecutive stream.Write calls? ... Is my SendConsecutively method flawed in any way which could cause such problems on the receiver-side (Also considering that message could be 3000 bytes, not just 6 like in the example code)?

根据套接字的内部实现,可能会有所不同。 At 可能导致两次连续发送或合并为一次发送。 NetworkStream sends calls Send of the underlying socket. This call is converted to the socket's Send() and then it really depends on the implementation of WinSock's send.

对于接收方而言,数据发送的准确程度并不重要。网络层可以将数据拆分为不同的块,而与发送的块的大小没有任何关系。