C++ - 创建位和半字节的整数

标签 c++ bit-manipulation

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

我面临的问题是我有一个 16 位整数值,它是由三个较小的值构建的:PCP 是 3 位长(因此 0 到 7),DEI 为 1 位,则 VLANID 为 12 位长 (0-4095)。 PCPDEI 一起构成前 4 位半字节,来自 VLANID 的 4 位添加到完成第一个字节,其余 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 字节整数
  • 第二个是确保位的顺序将被正确编码到线路上(因此字节的顺序不会相反)

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

最佳答案

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

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

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

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 位数量从主机内存字节顺序转换为网络接口(interface)字节顺序。

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

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 位字节,它们在之后具有兼容的数字表示形式考虑字节顺序等。


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


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

关于C++ - 创建位和半字节的整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19917272/

相关文章:

c++ - 如何在C++中初始化结构?

c++ - 从内存缓冲区初始化 C++ std::istringstream?

c - 按位除以 2 的倍数

用 mod 计算 rand 的动态范围

c++ - 将输出压缩成单行

c++ - 为什么 'this' 是指针而不是引用?

c++ - 按位与逻辑

java - 简而言之按位运算

c++ - SCIP和Visual Studio:错误LNK2001

javascript - 如何缩短表示字符序列的位数