有一个完整的背景(你真的不需要理解这个来理解这个问题,但它可能会有所帮助)我正在编写一个通过以太网发送数据的CLI程序,我希望添加VLAN标记和优先级标记到以太网标头。
我面临的问题是我有一个16位整数值,由三个较小的值构成:PCP
长3位(所以0到7),DEI
是1位,然后VLANID
长12位(0-4095)。 PCP
和DEI
一起形成前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
我真的有两个问题:
显然我已经尝试过这个代码来实现这个目标,但我真的不在我的深度,并希望从头开始看到某人的建议,而不是发布我到目前为止所做的事情以及有人建议如何改变它以可能难以阅读和冗长的方式执行所需的功能。
答案 0 :(得分: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进行发送和接收。但是,仍然应该使用正确的转换来简单地传达代码的意图。并且,谁知道,也许有一天你的代码将在PDP-11上运行,其中hton和ntoh不可互换。