【问题标题】:Network byte order on localhost本地主机上的网络字节顺序
【发布时间】:2014-01-18 09:02:49
【问题描述】:

我正在使用Winsock API 开发一个网络应用程序。每当我测试应用程序以发送几个字节时,例如在数组中:

char[0] = '1';
char[1] = '2';
char[2] = '3';
char[3] = '4';
char[4] = '\0';

我使用sendrecv 套接字在本地主机上运行的另一个应用程序上接收字节,然后字节顺序似乎根本没有改变。仅仅是因为我在 localhost 服务器上测试它吗?网络字节序是否会改变为:

char[0] = '\0';
char[1] = '4';
char[2] = '3';
char[3] = '2';
char[4] = '1';

如果我是通过 Internet 收到的?

【问题讨论】:

  • 据我了解(这对我来说是一个困惑的根源),我相信字节序只是序列化多字节对象时的一个因素。对象 within 中的字节会被重新排序。在这里,您发送的 chars 是单字节的,因此无法重新排列。这并不是说您的整个流都被重新排序。我不确定这一点,所以确认会很好。

标签: c++ windows winapi winsock endianness


【解决方案1】:

8 位字符数组和字符串不会受到网络字节顺序问题的影响。由于数组中的每个单独的 char 已经与其自身有序并与数组中的下一个 char 字节排序...受到影响的是 short、ints 和 longs。

此外,进行任何字节交换的不是 send 和 recv 调用。字节交换与处理器如何在内存中布局整数有关:

要了解字节顺序问题,请考虑 Intel 芯片上的以下代码:

// code that demonstrates how integer values are laid out in memory
int msg = 0x01020304;
char* buffer = (char*)&msg;
for (int x = 0; x < 4; x++)
    printf("%d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3]);

打印出来的结果是:

4 3 2 1

这表明 x86 处理器以您可能期望的相反顺序对整数的内存字节进行排序。最低有效字节或整数在内存中首先排序。这是小尾数。

在诸如旧的 Sun 工作站之类的 Sparc 芯片上,打印出相同的代码:

1 2 3 4

这是大端。

只有当您将数据写入磁盘或网络时,内存序列的字节顺序才重要。一旦另一台计算机读取另一台计算机写出的字节序列,读取该整数的 CPU 就会根据自己的字节顺序对其进行解释。

在将整数写入文件或流式传输到网络时,字节顺序通常很重要。如果应用程序开发人员没有进行转换,传递给 send() 的原始缓冲区会在处理器将字节放入内存时完全传输。请参阅htonl 和朋友了解有关在 Big Endian(也称为网络字节顺序)之间转换整数和短裤的更多详细信息。

【讨论】:

  • 但是如果我要发送一个整数说 int i = 256; 我需要将它转换为一个字符数组并将缓冲区传递给套接字。那么,字节序从何而来?
  • 在发送前将 i 转换为网络字节序。示例:int j = htonl(i); send(sock, (char*)&amp;j, sizeof(j), 0); 同样,在收到返回的整数时对收到的整数执行 ntohl() 调用。
  • 啊,我明白了。我是用另一种方式做的。使用位移将int i 转换为字符数组char buffer[4] 然后send(sock, buffer, sizeof(buffer), 0); 因此,当我在另一端将缓冲区解释为int 时,我没有注意到任何区别,因为所有内容都已经以字节为单位并且我发送它的方式。
  • 为什么我需要使用htonl 函数手动将其转换为大端字节序?我认为它会自动执行此操作。
  • 没有自动完成。但是,如果您网络上的所有节点都是 x86(小端序),那么您不必做任何这种字节顺序转换的事情。这是你的协议。如果您决定您的协议以小端序发送数据,那么您可以这样做。但是,如果您的协议需要与大端计算机互操作,那么您就需要处理字节顺序问题。大多数互联网协议都被编写为假定大端为网络字节顺序。
【解决方案2】:

只有当您查看字节流并决定解释数据时,字节序才会发挥作用。

例如,如果您有字节流:

'1' '2' '3' '4' '0'

然后您决定将数据解释为字符流,假设每个字符一个字节,您将得到一个包含字符串“1234”的数组。

但是,如果您决定将数据解释为 int,那么字节序就会发挥作用,因为您有多个字节用于对数据类型 (int) 进行编码。上述流的值具有十六进制值:

0x31 0x32 0x33 0x34 0x00

(0x31 是 '1' 等的十六进制值)

这就是字节序发挥作用的地方。您需要知道流中数据的字节顺序,以便在反序列化时成功地将其转换回正确的值。

【讨论】:

  • 但是如果我要发送一个整数说int i = 256;,我需要将它转换成一个字符数组并将缓冲区传递给套接字。那么,Endianness 是从哪里来的呢?
  • @cpx - 如果您想发送int 类型,那么您可以使用htonl (msdn.microsoft.com/en-us/library/windows/desktop/…) 将其转换为网络字节顺序。然后,当你读回它时,使用ntohl (msdn.microsoft.com/en-us/library/windows/desktop/…)
  • @cpx - 如果您知道您只会将数据发送到 x86 或 x64 硬件,那么您可以选择忽略字节顺序问题,因为发送方和接收方具有相同的字节序跨度>
  • 啊,我明白了。我是用另一种方式做的。使用位移将int i 转换为字符数组char buffer[4] 然后send(sock, buffer, sizeof(buffer), 0); 因此,当我在另一端将缓冲区解释为int 时,我没有注意到任何区别,因为所有内容都已经以字节为单位并且我发送它的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 2019-02-05
  • 2015-06-28
  • 2022-01-22
相关资源
最近更新 更多