9

关于htonlntohl。这两行代码中的任何一个何时评估为假。

 htonl(x) == ntohl(x);

 htonl(ntohl(x)) == htonl(htonl(x));

换句话说,这两个操作何时在同一台机器上不等效?我能想到的唯一情况是一台机器不能使用 2 的补码来表示整数。

原因主要是历史原因,是为了编码清晰,还是出于其他原因?

今天是否存在任何现代架构或环境,其中这些在同一台机器上与网络字节顺序之间的转换在任何一个方向上都不是相同的代码?

4

3 回答 3

8

多年前,我为 UNIVAC 1100 系列大型机编写了一个 TCP/IP 堆栈。这是一个 36 位、字可寻址的计算机体系结构,具有 1 的补码算法。

当这台机器进行通信 I/O 时,来自外部世界的 8 位字节将被放入每个 9 位四分之一字的低 8 位。因此,在这个系统上,ntohl() 会将每个四分之一字中的 8 位压缩到该字的低 32 位(前 4 位为零),以便您可以对其进行算术运算。

同样,htonl() 将获取一个字中的低 32 位并撤消此操作以将每个 8 位数量放入每个 9 位四分之一字的低 8 位。

因此,要回答最初的问题,此计算机架构上的 ntohl() 和 htonl() 操作彼此非常不同。

例如:

COMP*                                 . COMPRESS A WORD
          LSSL      A0,36             . CLEAR OUT A0
          LSSL      A1,1              . THROW AWAY TOP BIT
          LDSL      A0,8              . GET 8 GOOD ONE'S
          LSSL      A1,1              .
          LDSL      A0,8              .
          LSSL      A1,1              .
          LDSL      A0,8              .
          LSSL      A1,1              .
          LDSL      A0,8              .
          J         0,X9              .
.
DCOMP*                                . DECOMPRESS A WORD
          LSSL      A0,36             . CLEAR A0
          LSSL      A1,4              . THROW OUT NOISE
          LDSL      A0,8              . MOVE 8 GOOD BITS
          LSSL      A0,1              . ADD 1 NOISE BIT
          LDSL      A0,8              . MOVE 8 GOOD BITS
          LSSL      A0,1              . ADD 1 NOISE BIT
          LDSL      A0,8              . MOVE 8 GOOD BITS
          LSSL      A0,1              . ADD 1 NOISE BIT
          LDSL      A0,8              . MOVE 8 GOOD BITS
          J         0,X9              .

COMP 等效于 ntohl(),DCOMP 等效于 htonl()。对于那些不熟悉 UNIVAC 1100 汇编代码的人 :-) LSSL 是“左单移逻辑”一个由多个位置组成的寄存器。LDSL 是“Left Double Shift Logical”按指定计数的一对寄存器。所以 LDSL A0,8 将串联的 A0,A1 寄存器左移 8 位,将 A1 的高 8 位移到 A0 的低 8 位。

这段代码是在 1981 年为 UNIVAC 1108 编写的。几年后,当我们有一个 1100/90 并且它发展了一个 C 编译器时,我开始移植 BSD NET/2 TCP/IP 实现并实现 ntohl() 和 htonl () 以类似的方式。可悲的是,我从未完成过这项工作..

如果您想知道为什么某些 Internet RFC 使用术语“八位字节”,那是因为当时的某些计算机(如 PDP-10、Univacs 等)具有不是 8 位的“字节”。“八位字节”专门定义为 8 位字节。

于 2016-07-30T14:36:45.957 回答
7

我找不到 Posix 规范的原始草案,但最近在网上找到的一份有提示。

网络字节顺序可能不方便处理实际值。为此,将值存储为普通整数更为明智。这称为“主机字节顺序”。按主机字节顺序:

The most significant bit might not be stored in the first byte in address order.

**Bits might not be allocated to bytes in any obvious order at all.**

存储在 uint8_t 对象中的 8 位值不需要与主机字节顺序进行转换,因为它们具有相同的表示形式。可以使用 htonl()、htons()、ntohl() 和 ntohs() 函数转换 16 位和 32 位值。

有趣的是,以下声明是在讨​​论下做出的

POSIX 标准明确要求 8 位字符和二进制补码算法。

所以这基本上排除了我对 1 的补码机器实现的想法。

但是“任何明显的顺序”声明基本上表明 posix 委员会至少考虑了 posix/unix 在大端或小端以外的其他东西上运行的可能性。因此,不能排除将 htonl 和 ntohl 声明为不同的实现。

所以简短的回答是“htonl 和 ntohl 是相同的实现,但是两个不同函数的接口是为了将来与未知的兼容。”

于 2012-07-25T08:52:23.290 回答
-2

并非所有机器都具有相同的字节顺序,这些方法可以解决这个问题。假设“网络顺序”是大端。如果您有一台运行大端架构的机器并运行 ntohl,则输出将与输入相同(因为端序与网络相同)。如果您的机器是小端架构,ntohl 会将数据从大端转换为小端。关于 htonl 也可以这样说(必要时将主机数据转换为网络字节顺序)。要回答您的问题,当您在具有不同字节顺序的两台机器之间传输数据时,这两个操作并不等效。

于 2012-07-23T17:52:46.693 回答