【问题标题】:What is the purpose of the parameter passed to: IPAddress.HostToNetworkOrder?传递给:IPAddress.HostToNetworkOrder 的参数的目的是什么?
【发布时间】:2020-10-19 13:00:58
【问题描述】:

我正在查看一些其他人编写的代码,如下所示:

protected override void CloseAndSend(BinaryWriter request, uint lengthPos)
{
request.Seek((int)lengthPos, SeekOrigin.Begin);
request.Write(IPAddress.HostToNetworkOrder((int)(request.BaseStream.Length - lengthPos - sizeof(int))));

//more code here.
}

我知道 IPAddress.HostToNetworkOrder 确保使用适当的字节序,但是争论的目的是什么,即(int)(request.BaseStream.Length - lengthPos - sizeof(int)))?我读过这个:https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.hosttonetworkorder?view=netcore-3.1。我找不到任何例子,因此找不到问题的原因。我花了两个小时在谷歌上搜索它。

更新

这是填充 BinaryWriter 的代码(按正确顺序):

    byte EOL = 0;
        BinaryWriter writer = new BinaryWriter(new MemoryStream());
        writer.Write(UTF8Encoding.UTF8.GetBytes("API"));
        writer.Write(EOL);
        writer.Write((int)0);
        writer.Write(UTF8Encoding.ASCII.GetBytes("v100..155"));
        //request.BaseStream.Length - lengthPos - sizeof(int)=9
        //writer.BaseStream.Length=17

【问题讨论】:

  • 是转换为网络顺序的数字。你想写一个二进制形式的数字,但你不知道当前使用的是大端还是小端,所以如果需要,你可以使用这种方法转换为网络顺序(大端)。
  • @Evk,谢谢。您不必转换整个流吗?
  • 看起来区别是大端和小端。请参阅:docs.microsoft.com/en-us/dotnet/api/…。它在链接(或您的链接)的备注中。
  • @jdweng,我在我的问题中发布了该链接。你知道你不会转换整个流的任何场景吗?
  • 是的,如果数据不是全部 int32(4 字节数据)。

标签: c# network-programming endianness


【解决方案1】:

假设您需要通过网络传递一个 short\int\long 数字。通过网络您只能传递二进制数据,而这样的数字需要超过 1 个字节来表示。然后有一个问题——一个人可以从左到右或从右到左读取多个字节,结果会有所不同。这意味着协议应该指定这一点,或者如果未指定 - 应该有一个约定。网络协议存在这样的约定,如果没有另外说明,则使用大端。

现在,BinaryWriter 始终使用当前架构的字节序(例如,您可以通过 BitConverter.IsLittleEndian 进行检查)。如果您当前的架构是 little endian,并且您需要通过网络写入一个数字(例如 TCP 协议中常见的以下消息长度)怎么办?假设长度为 1,为此长度保留的字节数为 2(因此,总长度为 short)。如果你只是这样做

request.Write(length)

在 little endian 架构上,两个字节 01 - 00 会通过网络写入。但是,大端的 01 - 00 字节代表完全不同的数字 - 256。线路另一端的消费者,如果他认为应该采用大端 - 将读取两个字节,并将它们解释为完全不同的消息长度。

所以为了避免这种情况 - 如果需要,您可以将所述数字转换为大端:

request.Write(IPAddress.HostToNetworkOrder(length))

如果您使用小端,HostToNetworkOrder 会将短 1 转换为 256,然后在这种情况下也使用小端的 BinaryWriter 将写入字节 00 - 01(以小端表示 256)。如果您使用大端序,那么 HostToNetworkOrder 将返回 1,BinaryWriter 将再次通过线路写入相同的 00 - 01 字节,以确保一致的表示。

跟进您的更新:

有问题的代码以 UTF-8(3 个字节)写入字符串“API”,然后是一些 EOL 字节(现在我们有 4 个字节),然后通过写入零整数来保留 4 个字节。它继续写入另一个东西,然后返回到这个保留的 4 字节整数(包含所有零字节)的位置。然后,它计算该 4 字节整数(“v100..155”等)之后的内容的长度,并将该长度写入保留位置。它以一种非常奇怪的方式只写入以下数据的长度,这在大多数 TCP 协议中很常见。

如果这是整个代码,那么简单的方法就是:

    byte EOL = 0;
    BinaryWriter writer = new BinaryWriter(new MemoryStream());
    writer.Write(UTF8Encoding.UTF8.GetBytes("API"));
    writer.Write(EOL);
    var data = UTF8Encoding.ASCII.GetBytes("v100..155");
    writer.Write(IPAddress.HostToNetworkOrder(data.Length));
    writer.Write(data);

【讨论】:

  • 谢谢。正如我在问题中所说,我理解字节序的概念。我不明白的是为什么只有部分流似乎被转换了(请参阅传递给的论点:IPAddress.HostToNetworkOrder。您知道您不会转换整个流的任何情况吗?
  • @w0051977 字节序不适用于整个流,仅适用于其中编码的数据片段。假设您通过线路传递 3 个两字节数字。然后在一个字节序下是a-b-c-d-e-f,而在另一个字节序下:b-a-d-c-f-e,它永远不会是f-e-d-c-b-a。在这种情况下,一个 数字 正在被转换(数字是(int)(request.BaseStream.Length - lengthPos - sizeof(int)) 的结果),然后这个数字被写入BinaryWriter。如果该表达式的结果是 10,那么数字 10 将被写入 BinaryWriter,确保适当的字节序。
  • 您能否提供一个示例,说明您只在答案中转换部分流?
  • @w0051977 您问题中的代码不会转换任何内容。它寻找流中的指定位置并在那里写入一个特定的数字,就是这样。所以我不明白你在问什么。
  • @w0051977 是的,这可能是空终止字符串,原因与上述相同:对于未知长度的字符串,您可以在它们后面跟随零字节以指示字符串的结尾。对于任意二进制数据(不是字符串),你不能这样做,因为零字节在数据本身内部是完全有效的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2017-03-23
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
相关资源
最近更新 更多