C 轻便紧凑的钻头包装

C 轻便紧凑的钻头包装,c,network-programming,bit-manipulation,C,Network Programming,Bit Manipulation,假设我有三个unsigned int,{a,b,c,d,我想分别用非标准长度{9,5,7,11}打包。我希望制作一个网络数据包(unsigned char pkt[4]),我可以将这些值打包到另一台机器上,并在另一台机器上使用相同的头文件可靠地解包,而不考虑endianness 我所读到的关于使用压缩结构的所有内容都表明位顺序是不可预测的,所以这是不可能的。这就给我留下了位设置和位清除操作,但我对如何确保endianness不会给我带来问题没有信心。以下内容是否足够,或者我是否应该分别遇到a和d

假设我有三个
unsigned int
,{
a
b
c
d
,我想分别用非标准长度{9,5,7,11}打包。我希望制作一个网络数据包(
unsigned char pkt[4]
),我可以将这些值打包到另一台机器上,并在另一台机器上使用相同的头文件可靠地解包,而不考虑endianness

我所读到的关于使用压缩结构的所有内容都表明位顺序是不可预测的,所以这是不可能的。这就给我留下了位设置和位清除操作,但我对如何确保endianness不会给我带来问题没有信心。以下内容是否足够,或者我是否应该分别遇到
a
d
的结尾问题

void pack_pkt(uint16_t a, uint8_t b, uint8_t c, uint16_t d, uint8_t *pkt){
    uint32_t pkt_h = ((uint32_t)a & 0x1FF)      // 9 bits
                 | (((uint32_t)b & 0x1F) << 9)  // 5 bits
                 | (((uint32_t)c & 0x3F) << 14) // 7 bits
                 | (((uint32_t)d & 0x7FF) << 21); //11 bits
    *pkt = htonl(pkt_h);
}

void unpack_pkt(uint16_t *a, uint8_t *b, uint8_t *c, uint16_t *d, uint8_t *pkt){
    uint32_t pkt_h = ntohl(*pkt);
    (*a) = pkt_h & 0x1FF;
    (*b) = (pkt_h >> 9) & 0x1F;
    (*c) = (pkt_h >> 14) & 0x3F;
    (*d) = (pkt_h >> 21) & 0x7FF;
}
void pack_pkt(uint16_t a、uint8_t b、uint8_t c、uint16_t d、uint8_t*pkt){
uint32_t pkt_h=((uint32_t)a&0x1FF)//9位
|((uint32_t)b&0x1F)>14)和0x3F;
(*d)=(pkt_h>>21)和0x7FF;
}

如果是这样,我还可以采取哪些措施来确保可移植性?

对于打包和打包,我建议使用如下结构

  • 请记住,结构的大小在其他机器中是不同的,比如8位系统和32位系统。我们在结构中称之为padding,所以您可以使用pack来确保发送器和接收器中的结构大小相同
typedef结构{
uint8_t A;
uint8_t B;
uint8_t C;
uint8_t D;
}我的包;
现在,您可以将这个结构流化为字节流,例如SerialPort或UART或其他内容 在接收器中,您可以打包字节以获取数据

请参见以下功能

void传输包(MyPacket*packet){
int len=sizeof(MyPacket);
uint8_t*pData=(uint8_t*)数据包;
而(长度-->0){
//按1发送字节1
传输字节(*pData++);
}
}
无效接收数据包(我的数据包*数据包){
int len=sizeof(MyPacket);
uint8_t*pData=(uint8_t*)数据包;
而(长度-->0){
//接收字节1乘1
*pData++=receiveByte();
}
}
请记住,字节中的位顺序在任何地方都是相同的,但您必须检查字节顺序,以确保数据包不会在接收器中丢失

例如,如果数据包的大小为4字节,则首先发送低字节 您必须在接收器中接收低字节


在您的代码中,您可以在uint8_t*指针中获得数据包,但数据包的实际大小是uint32_t,是4个字节

带位字段的结构实际上对此毫无用处,因为它们的字段顺序甚至填充规则都不一致

我会不会分别遇到
a
d
的结尾问题

void pack_pkt(uint16_t a, uint8_t b, uint8_t c, uint16_t d, uint8_t *pkt){
    uint32_t pkt_h = ((uint32_t)a & 0x1FF)      // 9 bits
                 | (((uint32_t)b & 0x1F) << 9)  // 5 bits
                 | (((uint32_t)c & 0x3F) << 14) // 7 bits
                 | (((uint32_t)d & 0x7FF) << 21); //11 bits
    *pkt = htonl(pkt_h);
}

void unpack_pkt(uint16_t *a, uint8_t *b, uint8_t *c, uint16_t *d, uint8_t *pkt){
    uint32_t pkt_h = ntohl(*pkt);
    (*a) = pkt_h & 0x1FF;
    (*b) = (pkt_h >> 9) & 0x1F;
    (*c) = (pkt_h >> 14) & 0x3F;
    (*d) = (pkt_h >> 21) & 0x7FF;
}
a
d
的尾数无关紧要,它们的字节顺序从不使用
a
d
不会重新解释为原始字节,只使用或分配它们的整数值,在这些情况下,endianness不会进入图片

不过还有一个问题:
uint8_t*pkt
*pkt=htonl(pkt_h)结合使用
表示只保存最低有效字节(不管它是由小端计算机还是大端计算机执行,因为这不是重新解释,而是隐式转换)
uint8_t*pkt
本身是正常的,但随后产生的4字节组必须复制到它所指向的缓冲区中,不能一次性全部分配
uint32_t*pkt
可以使这样一个任务在不丢失数据的情况下工作,但这会降低该函数的使用方便性

类似地,在解包pkt中,当前仅使用一个字节的数据

当这些问题得到解决时,它应该是好的:

void pack_pkt(uint16_t a, uint8_t b, uint8_t c, uint16_t d, uint8_t *buffer){
    uint32_t pkt_h = ((uint32_t)a & 0x1FF)      // 9 bits
                 | (((uint32_t)b & 0x1F) << 9)  // 5 bits
                 | (((uint32_t)c & 0x3F) << 14) // 7 bits
                 | (((uint32_t)d & 0x7FF) << 21); //11 bits
    uint32_t pkt = htonl(pkt_h);
    memcpy(buffer, &pkt, sizeof(uint32_t));
}

void unpack_pkt(uint16_t *a, uint8_t *b, uint8_t *c, uint16_t *d, uint8_t *buffer){
    uint32_t pkt;
    memcpy(&pkt, buffer, sizeof(uint32_t));
    uint32_t pkt_h = ntohl(pkt);
    (*a) = pkt_h & 0x1FF;
    (*b) = (pkt_h >> 9) & 0x1F;
    (*c) = (pkt_h >> 14) & 0x3F;
    (*d) = (pkt_h >> 21) & 0x7FF;
}

最初的方法并不坏,这只是另一种选择,需要考虑的事情。因为没有任何东西会被重新解释,所以endianness根本不重要,这可能会增加对代码正确性的信心。当然,作为一个缺点,它需要更多的代码来完成同样的事情。顺便说一句,尽管手动解构
uint32\t
并存储4个单独的字节看起来需要大量工作,但GCC可以将其转换为
bswap
和32位存储。另一方面,Clang错过了这个机会,其他编译器也可能错过,因此这并非没有缺点。

Hi@ali,感谢您的输入。这并没有真正解决从小端系统发送到大端系统的问题。我的设置还包括一端的32位计算机和另一端的64位计算机。如果两个端系统使用不同的端系统,如小端系统和大端系统,我们必须使用字节交换函数,对于反向uint16或uint32变量,但我们的uint8变量相同,记住bits变量总是存储在uint32这样的包中,那么我可以确保打包结构的打包顺序在两个设备上是一致的吗?据我所知,标准中未定义订购,因此不可移植。请参见@MarcianoPreciado中的第一个答案。该答案不使用位字段,因此定义了顺序-但对于您的目的来说,它也是无用的,因为它没有太多地解决大小异常的FieldsHanks的使用问题!你的解释和替代方法确实帮助我理解了这一切是如何运作得非常好的。