double类型数据的网络字节序转换

最近,将一套linux开发程序移植到另外一块开发板上时,遇到了数据传输不正确的情况(主要是浮点数)。经过错误定位发现,程序中涉及到了网络通信,且所用开发板的处理器为小端模式,之前的处理器为大端模式,因此两个版本的程序中的数据有不一样的字节序,在数据传输的过程中需要进行网络字节序转换,将转换后的数据发送到网络中(将主机序转换成网络序)。

1、基本概念:
网络字节顺序 TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian(大端)排序方式。
主机字节顺序不同的 CPU 有不同的字节序类型,这些字节序是指整数在内存中保存的顺序 ,最常见的有两种:
小端(Little endian) :数据的低位字节放在内存的低处
大端(Big endian) :数据的低位字节放在内存的高处
如果两个不同主机之间的字节序不一样,在进行网络通信的过程中,需要统一转换成网络字节序,这样才能进行通信。因此,网络序能够保证字节序不同的主机之间数据传输的正确性。

2、对于两个字节和四个字节整形数据
对于两个字节和四个字节整形数据,可以直接调用htons()和htonl()库函数将主机序转换成网络序。
htons():将一个16位数从主机字节顺序转换成网络字节顺序。
htonl():将一个32位数从主机字节顺序转换成网络字节顺序。

3、对于double类型的浮点数

- 3.1 八个字节整型数据的字节序转换

程序中所涉及的浮点数为double类型,所占字节数为8,因此,需要自己编写相应的函数来实现double类型数据的字节序转换。由于double类型占了八个字节, 并没有相应的库函数能够将八个字节的数据进行字节序转换,因此,需要构造自己的八字节数据的字节序转换函数,在此处定义了一个宏来实现该函数功能,如下所示:

#define htonll(x)    (((long long int)x & 0x00000000000000ff) << 7*8)|\
							 (((long long int)x & 0x000000000000ff00) << 5*8)|\
							 (((long long int)x & 0x0000000000ff0000) << 3*8)|\
							 (((long long int)x & 0x00000000ff000000) << 1*8)|\
							 (((long long int)x & 0x000000ff00000000) >> 1*8)|\
							 (((long long int)x & 0x0000ff0000000000) >> 3*8)|\
							 (((long long int)x & 0x00ff000000000000) >> 5*8)|\
							 (((long long int)x & 0xff00000000000000) >> 7*8)

此外,也可以利用htonl函数来构成八字节数据的字节序转换的功能。
上述宏的功能是实现了 八个字节整形数据的字节序转换的功能,我们可以在vs中进行测试,浮点数的十六进制表示比较麻烦,在vs中可以方便地看到整数和浮点数的十六进制表示形式。定义一个long long int型的整型变量datain,对其赋值为0x1234567890abcdef,并调用我们定义的htonll()函数,代码如下:

long long int datain = 0x1234567890abcdef;
long long int dataout = htonll(datain);

在vs中打断点,对变量内存进行查看,datain和dataout变量的内存情况分别对应于图1和图2:
图1 datain内存情况
图1 datain内存情况
图2 dataout内存情况
图2 dataout内存情况

从上图中可以看出,我的电脑中数据是小端存储的,且经过htonll()函数,数据的字节序被转换了。

- 3.2 八个字节浮点数的字节序转换

在此基础上,我们对double类型的浮点数进行字节序转换,hotnll()函数在进行字节序转换的时候,输入为long long int型的数据,因此,在对double类型的浮点数进行转换时,需要将其转换为long long int类型的整型数据。需要注意的是,在此时不能使用强制类型转换,强制类型转换会改变原始数据的值,最后会得到错误的结果。
以浮点数151111111112.5为例,在进行字节序转换之前,我们需要对double类型的数据做如下处理,使送到htonll()函数中的数据在内存中的存储情况不发生改变:

double datain = 151111111112.5;//定义一个大的浮点数,便于观察内存中字节序情况
long long int temp = *((long long int*)&datain);//将原始double类型数据的地址转换成long long int*指针类型,并取出该指针所指向的内容
long long int dataout = htonll(temp);

图3是datain在内存中的存储情况
datain内存存储情况
图3 datain内存存储情况
将原始double类型数据的地址转换成long long int*指针,并取出该指针所指向的内容赋给变量temp,图4是该变量的内存存储情况
temp内存存储情况
图4 temp内存存储情况
从图4中可以看出,这种转换方式不会改变数据在内存中的存储情况。
图5是dataout的内存存储情况
dataout内存存储情况
图5 dataout内存存储情况
因此,利用该方法可以实现double类型数据的字节序转换,一般情况下,将八个字节改为四个字节,可以进行float类型数据的字节序转换。

你可能感兴趣的:(C/C++,C/C++,linux,网络编程,网络字节序转换)