概念-什么是字节序
计算机中程序解析内存中的多字节数据时需要确定低地址存放低位(小端字节序),还是高地址存放低位(大端字节序),实际操作中不同的计算机并没有采用一致的处理方式,这就会造成同样的一段内存内容在被解释为数字时,会是完全不用的2个数。例如:在内存中同样存储2个字节数据,采用不同的解析方式得到的数值是不一样的,比如内存中存放
addr+0 | 0x01
addr+1 | 0x02
小端字节序解释为:0x0201
大端字节序解释为:0x0102
这种情况下,需要2个主机交换信息时,如果需要解释多字节数值,就会出现不一致的问题,为了解决这个问题大家达成了一个约定,默认在网上传输的数据采用大端字节序(即网络字节序),在发送端发送多字节数据时使用hton转换为大端字节序,在数据接收端接收到多字节数据时使用ntoh转换为本地字节序,从而避免因为字节序不一致导致的数据一致性问题。
转换
数据转换原理分析:
源数据:
0x0102030405060708
大端主机内存数据:
addr+0 | 0x01 |
addr+1 | 0x02 |
addr+2 | 0x03 |
addr+3 | 0x04 |
addr+4 | 0x05 |
addr+5 | 0x06 |
addr+6 | 0x07 |
addr+7 | 0x08 |
小端主机内存数据:
addr+0 | 0x08 |
addr+1 | 0x07 |
addr+2 | 0x06 |
addr+3 | 0x05 |
addr+4 | 0x04 |
addr+5 | 0x03 |
addr+6 | 0x02 |
addr+7 | 0x01 |
假设收到的数据为大端字节序存储的0x0102030405060708,本机使用字节序为小端字节序。
方法1:
#直接使用64位的转换函数把大端字节序数据转为小端字节序。
be64toh
转换后内存数据:
addr+0 | 08 |
addr+1 | 07 |
addr+2 | 06 |
addr+3 | 05 |
addr+4 | 04 |
addr+5 | 03 |
addr+6 | 02 |
addr+7 | 01 |
方法2:
#把64位的数据分为低32位和高32位数据分别使用be32toh后组合为完整的64位数据。
be32toh(p32[0])<<32 | be32toh(p32[1])
内存数据:
addr+0 | 0x01 |
addr+1 | 0x02 |
addr+2 | 0x03 |
addr+3 | 0x04 |
解析为数值:0x04030201 ---- be32toh ----> 0x01020304
addr+4 | 0x05 |
addr+5 | 0x06 |
addr+6 | 0x07 |
addr+7 | 0x08 |
解析为数值:0x08070605 ---- be32toh ----> 0x05060708
0x01020304 << 32 | 0x05060708
0x0102030405060708
转换后内存数据:
addr+0 | 08 |
addr+1 | 07 |
addr+2 | 06 |
addr+3 | 05 |
addr+4 | 04 |
addr+5 | 03 |
addr+6 | 02 |
addr+7 | 01 |
网络程序常用转换代码:
#include <arpa/inet.h>
#include <stdint.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);
其它程序常用转换代码:
#define _BSD_SOURCE /* See feature_test_macros(7) */
#include <endian.h>
#include <stdint.h>
uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);
uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);
uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);