2

对于完整的背景(您并不需要真正理解这一点来理解问题,但它可能会有所帮助)我正在编写一个通过以太网发送数据的 CLI 程序,我希望将 VLAN 标记和优先级标记添加到以太网标头。

我面临的问题是我有一个 16 位整数值,它由三个较小的值构成:PCP3 位长(所以 0 到 7),DEI1 位,然后VLANID是 12 位长(0-4095)。PCPDEI一起形成第一个 4 位半字节,4 位从VLANID添加完成第一个字节,其余 8 位从VLANID形成整数的第二个字节。

11123333 33333333

1 ==PCP位,2 ==DEI位,3 ==VLANID

让我们假设PCP== 5,二进制是 101,DEI== 0,和VLANID== 164,二进制是 0000 10100011。首先我需要将这些值编译在一起,如下所示:

10100000 10100101

然后我面临的问题是,当我将此整数复制到要编码到线路(以太网介质)上的缓冲区中时,位顺序会发生如下变化(我在将整数复制到线路之前以二进制形式打印出整数并使用wireshark在电线上捕获它以进行比较):

内存中的位顺序:abcdefgh 87654321

线上的位顺序:8765321 abcdefgh

我真的有两个问题:

  • 第一个是通过将三个较小的整数“粘”在一起来创建 2 字节整数
  • 第二个是确保位的顺序是正确编码到线路上的(所以字节不是相反的顺序)

显然,我已经尝试过这段代码来做到这一点,但我真的超出了我的深度,希望从头开始看到某人的建议,而不是发布我到目前为止所做的事情以及有人建议如何将其更改为以可能难以阅读和冗长的方式执行所需的功能。

4

1 回答 1

3

问题是字节顺序,而不是位顺序。内存中的位实际上没有顺序,因为它们不能单独寻址,并且传输介质负责确保传输的离散实体(在这种情况下为八位字节)以与发送时相同的形状到达。

另一方面,字节是可寻址的,传输介质不知道您是发送一个不需要重新排序的字节字符串,还是一个四字节整数,这可能需要在接收端进行一个字节排序,而另一个在发件人的。

出于这个原因,网络协议有一个声明的“字节顺序”,所有发送者和接收者都应该在其中转换他们的数据。通过这种方式,不同本地字节顺序的网络主机可以透明地发送和检索数据。

POSIX 定义了一些函数来进行所需的转换:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

“n”和“h”分别代表“网络”和“主机”。因此 htonl 将 32 位数量从主机的内存字节顺序转换为网络接口的字节顺序。

每当您准备通过网络发送缓冲区时,您应该将其中的每个值从主机的字节顺序转换为网络的字节顺序,并且任何时候处理接收数据的缓冲区时都应该转换其中的数据从网络的排序到主机的排序。

struct { uint32_t i; int8_t a, b; uint16_t s; } sent_data = {100000, 'a', 'b', 500};

sent_data.i = htonl(sent_data.i);
sent_data.s = htons(sent_data.s);

write(fd, &sent_data, sizeof sent_data);

// ---

struct { uint32_t i; int8_t a, b; uint16_t s; } received_data;

read(fd, &received_data, sizeof received_data);

received_data.i = ntohl(received_data.i);
received_data.s = ntohs(received_data.s);

assert(100000 == received_data.i && 'a' == received_data.a &&
       'a' == received_data.b && 500 == received_data);

虽然上面的代码仍然做了一些假设,比如发送方和接收方都使用兼容的字符编码(例如,它们都使用 ASCII),它们都使用 8 位字节,在考虑字节后它们具有兼容的数字表示订购等


不关心可移植性并且仅在远程主机上与自己进行互操作的程序可能会跳过字节顺序以避免性能成本。由于所有主机都将共享相同的字节顺序,因此它们根本不需要转换。当然,如果程序执行此操作,然后需要移植到具有不同字节顺序的平台,则网络协议必须更改,或者程序必须处理既不是网络顺序也不是主机顺序的字节顺序.


今天,唯一常见的字节顺序只是相互颠倒,这意味着 hton 和 ntoh 都做同样的事情,人们也可以使用 hton 来发送和接收。但是,仍然应该使用正确的转换来简单地传达代码的意图。而且,谁知道呢,也许有一天您的代码会在 hton 和 ntoh 不可互换的 PDP-11 上运行。

于 2013-11-11T22:51:40.137 回答