【无标题】

大端和小端机器通信

在通信前,通常需要约定以何种方式(大端或小端)进行通信,通常网络使用大端序进行通信,在其他系统中,由于大端机较少使用,也可约定使用小端序进行通信。本文默认使用小端序进行通信。

一、对于基本类型,一般使用 htons, htonl, htonll 分别对16位类型、32位类型、64位类型进行字节序反转,以下是 Darwin 中关于这三个函数的标准库实现。

 #define htons(x) \
     ((__uint16_t)((((__uint16_t)(x) & 0xff00U) >> 8) | \
 	        (((__uint16_t)(x) & 0x00ffU) << 8)))
 
 #define htonl(x) \
     ((__uint32_t)((((__uint32_t)(x) & 0xff000000U) >> 24) | \
 	        (((__uint32_t)(x) & 0x00ff0000U) >>  8) | \
 	        (((__uint32_t)(x) & 0x0000ff00U) <<  8) | \
 	        (((__uint32_t)(x) & 0x000000ffU) << 24)))
 
 #define htonll(x) \
     ((__uint64_t)((((__uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \
 	        (((__uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
 	        (((__uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
 	        (((__uint64_t)(x) & 0x000000ff00000000ULL) >>  8) | \
 	        (((__uint64_t)(x) & 0x00000000ff000000ULL) <<  8) | \
 	        (((__uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
 	        (((__uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
 	        (((__uint64_t)(x) & 0x00000000000000ffULL) << 56)))
 

二、对于结构体,可以采取两种方式,假定小端机中结构体定义如下:

typedef union {
	struct fields {
	    uint8_t a;
	    uint32_t b1: 8;
	    uint32_t b2: 12;
	    uint32_t b3: 12;
	    uint16_t c;
	} fields;
	char data[sizeof(struct fields)];
} basic_data;

1. 大端机中,结构体定义完全反转,在接收和发送结构体时反转整个结构体字节序。

 typedef union {
 	struct fields {
 #ifdef BIG_ENDIAN
 	    uint16_t c;
 	    uint32_t b3: 12;
 	    uint32_t b2: 12;
 	    uint32_t b1: 8;
 	    uint8_t a;
 #else
 	    uint8_t a;
 	    uint32_t b1: 8;
 	    uint32_t b2: 12;
 	    uint32_t b3: 12;
 	    uint16_t c;
 #endif
 	} fields;
 	char data[sizeof(struct fields)];
 } basic_data;
使用 BIG_ENDIAN 宏表示该反转定义仅在大端机中失效。

大端机在发送和接收该结构体时,使用swap_bytes_array 反转整个结构体
 void swap_bytes_array(void *ptr, size_t len) {
 #ifdef BIG_ENDIAN
     unsigned char *start, *end, temp;
     start = (unsigned char *)ptr;
     end = start + len - 1;
     while (start < end) {
         temp = *start;
         *start = *end;
         *end = temp;
 
         start++;
         end--;
     }
 #endif
 }
 
 void main() {
   basic_data data;
   data = 接收到的小端序数据;
   swap_bytes_array(&data, sizeof(data));
 }
反转字节序 + 反转结构体定义,便可以正常读取数据。

2. 大端机中,结构体定义中仅反转位域顺序,在接收和发送结构体时分别反转结构体中各个成员。

在这种解决方案中,只需要对位域进行反转定义,对于无位域结构体,大小端结构体定义一致,但对于较复杂结构体,这种解决方案较为繁琐。
typedef union {
	struct fields {
	    uint16_t a;
#ifdef BIG_ENDIAN
	    uint32_t b3: 12;
	    uint32_t b2: 12;
	    uint32_t b1: 8;
#else
    	uint32_t b1: 8;
	    uint32_t b2: 12;
	    uint32_t b3: 12;
#endif
      uint8_t c;
	} fields;
	char data[sizeof(struct fields)];
} basic_data;

void main() {
  basic_data data = 接收到的数据;
  // 交换 uint16_t 数据字节序
  data.a = htonl(data.a);
  // 由于位域不可直接寻址,故使用结构体中相邻成员找到位域起始地址
  // 将位域视为字节数组进行反转
  swap_bytes_array((void *)(&data.a + 1), sizeof(uint32_t));
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值