【问题标题】:C++ - Creating an integer of bits and nibblesC++ - 创建一个整数位和半字节
【发布时间】:2013-11-11 22:31:11
【问题描述】:

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

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

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 字节整数
  • 第二个是确保位的顺序是正确编码到线路上的顺序(因此字节的顺序不是相反的)

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

【问题讨论】:

  • 查看 ntoh 和 hton(网络到主机和主机到网络)。问题是大/小端。
  • 使用hton 系列函数来处理这个问题。

标签: c++ bit-manipulation


【解决方案1】:

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

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

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

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 上运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 1970-01-01
    • 1970-01-01
    • 2018-03-14
    相关资源
    最近更新 更多