为什么通过网络发送的数据要转换为网络字节顺序?

3

我不确定如何使用hton()。理论上,任何发送到网络上的数据都应该采用网络字节(即大端)格式。假设客户端A支持大端,而B支持小端。我正在从A向B发送多字节数据,那么在网络中我们需要使用htonl()htons()将数据转换为网络字节顺序。由于客户端A已经是大端,所以htonl()htons()返回相同的输出。但是B是小端,因此这些函数会颠倒顺序。鉴于此,当大端和小端机器需要通信时,我们如何说遵循共同的格式(即大端)是一种解决问题的方法?

2个回答

7
我会尝试另一种方式,展示整个流程:
0x44332211通过网络发送时,始终呈现为44 33 22 11。发送方的htonl()确保它们要么反转字节顺序(在LE机器上),要么只是按原样保留(在BE机器上)。接收方使用ntohl()44 33 22 11转换为0x44332211,同样地,无论是反转还是按原样保留。
提到的{hton,ntoh}{l,s}()函数以便更好地进行可移植编程:无论程序在LE还是BE机器上运行,它们总是按照应该工作的方式工作。因此,即使在BE机器上也应调用这些函数,即使它们是noop。
示例:
A(BE)想将0x44332211发送给B(LE)。
  1. A在内存中以44 33 22 11的形式拥有该数字。
  2. A调用htonl(),因为程序被编写成可移植的。
  3. 数字仍然表示为44 33 22 11并通过网络发送。
  4. B收到44 33 22 11并通过ntohl()将其转换为数字。
  5. ntohl()44 33 22 11获取由11 22 33 44表示的值,然后将其放入相应的变量中-结果为所需的0x44332211
再次,始终调用这些函数的需要使您免于考虑正在编程的机器类型-只需针对所有类型的机器进行编程,并在需要时分别调用每个函数。
不知道A或B是BE还是LE的相同示例可以表述如下:
  1. A在内存中拥有数字0x44332211
  2. A调用htonl(),以便数字作为44 33 22 11发送到网络。这是通过反转或保留来决定主机B的字节顺序。
  3. B收到44 33 22 11并通过ntohl()将其转换为数字。此功能是否反转取决于主机B的字节顺序。
  4. B得到所需的0x44332211值。

我还有几个疑问。hton()是否总是表示转换为Bigendian格式,而ntoh()则表示总是转换为little endian格式?因为主机可以是little endian或bigendian。这是因为在上述情况中B是LE格式,所以在第4步中ntohl(44 33 22 11)会得到11 22 33 44吗?如果B是BE格式,那么ntohl(44 33 22 11)的结果会是44 33 22 11吗?其他部分对我来说很清楚了。感谢您提供如此好的解释。 - Subi Suresh
@SubiSuresh,“hton”表示“主机到网络”,“ntoh”表示“网络到主机”。它们是否转换以及转换成哪种格式取决于本地数据格式。在BE机器上,它们都不做任何事情,在LE机器上,它们都会进行转换。 - glglgl
@gigl>请不要介意,我也有同样的疑问,仍然不清楚。我理解到第4步为止。B接收到44 33 22 11。因此,在B的内存位置中,它将被存储为11 22 33 44(假设起始地址为100,则100包含11,102包含22,104包含33,106包含44,因为B是LE,而在读取时,B将从最高的内存地址即106读取,如44 33 22 11)。那么,在第4步中,您提到ntohl()的必要性是什么,请澄清一下。对于像我这样的初学者来说,这将非常有帮助。 - Subi Suresh
@SubiSuresh 在第4步中,由于主机B是LE,函数/宏执行还原操作。再说一遍:您需要在任何地方调用该函数,因为您想要编写可移植的程序,并且编译器及其环境知道是否实际执行某些操作。 - glglgl
我想问您一个细节以澄清:在第3步中,数字44 33 22 11是如何发送的:我们首先发送11,然后是22,33,44(从左到右发送流的自然视图),因此接收者会先收到11,然后是22,33,44,还是反过来?另外,当我们发送像0x78这样的字节时,它在二进制中是0111_1000,我们是否从最右边的位开始发送? - mercury0114
@mercury0114,正如我明确提到的将字节拆分,这些字节按给定顺序发送,因此为44 33 22 11。关于位顺序,作为程序员,您不必担心它,因为它可能因技术而异。 - glglgl

1
我认为你可能认为客户端B看到的字节顺序是错误的。与客户端A相比,字节将以相反的顺序出现,但这是因为客户端A从客户端B反向解释整数;最终两者仍将将其解释为相同的数字。例如,一个机器将数字4表示为00 00 00 04。另一个机器将其表示为04 00 00 00,但无论如何,它们仍将视其为4--如果您将1添加到其中,您将得到00 00 00 0505 00 00 00。存在hton/ntoh函数的原因是没有办法查看一个数字并知道它是大端还是小端,因此接收方无法确定如何解释字节。

@SubiSuresh 接收器按发送方发送的顺序逐个字节读取数据。如果 A 发送 44 33 22 11,B 将读取 44 33 22 11,并且需要知道这些字节的字节序(它知道所有网络数据都是大端序)。由于 B 是小端主机,它将翻转字节并将 11 22 33 44 存储在内存中,这代表与 A 的 44 33 22 11 相同的数字,因为 A 和 B 解释数字的方式不同。 - Michael Mrozek
@SubiSuresh 11 22 33 44 在大端和小端都是一个有效的数字。在大端中,它转换为十进制数287454020;在小端中,它是1144201745。系统需要知道数字所在的字节序,才能确定其实际值。 - Michael Mrozek
@Micheal Morozek>请你帮忙检查一下这个注释,我理解得对还是错,这样我才能继续进行。 - Subi Suresh
1
@SubiSuresh:每个人都认为,如果你需要从网络中读取4字节整数,则高位字节先到达,然后是中间两个,最后是低位字节。接收机上的软件清楚地知道如何将网络上接收到的数据重新组装成正确的主机格式,在这种情况下可以使用ntohl()。在某些机器上,它可能是无操作的;在其他机器上,它需要一些位操作。 - Jonathan Leffler
@glglgl>我将通过示例向您解释。考虑服务器A(大端)向B(小端)发送4字节整数数据0x44332211。因此,B将其解释为0x11223344,这是一个不同的数字。所以现在根据理论,我们必须在网络上传输时使用网络字节序。因此htons(0x44332211)仍将给出相同的0x11223344,因为A是大端。现在我的问题是B将其解释为不同的数字0x11223344。那么,在这里使用htonl()有什么用呢?请帮忙 @glglgl - Subi Suresh
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接