前言
在网络编程中,我们经常会遇到不同主机间通信时,由于主机CPU架构不同而需要约定传输大小端格式问题。造成大小端问题的主要原因是不同主机的CPU存储数据的方式不同。例如:2字节无符号短整型数字1可用2进制表示如下:00000000 00000001有些CPU按照以上顺序将数据存储到内存中,而有些数据则以倒序的顺序存储数据,如下所示:00000001 00000000如果在网络编程中,不考虑内存存储方式问题,直接将该无符号短整型数字拷贝到发送缓存区中发送,若对方主机CPU存储方式与本机CPU存储方式相同,则不会出现异常情况;若对方主机存储方式与本机存储方式不同,则会解析出现问题。
一、大端序和小端序
从上述中可知,CPU向内存中保存数据的方式有两种:大端序(Big Endian):高位字节存放到低地址位置(即高字节在前,低字节在后)例如:短整型数值0X1122,存储到内存地址(例如内存地址为:0x5001,0x5002)中时,按照大端存储方式存储为:0X11(地址为0x5001),0X22(地址为0x5002);小端序(Little Endian):高位字节存储在高位地址位置(即低字节在前,高字节在后)还以上述例子,按照小端存储方式存储为:0X22(地址为0x5001),0X11(地址为0x5002)。通俗的说法为:大端的存储时,字节间的顺序没有发生改变;小端存储时,字节间的顺序发生了改变。另外,大小端问题只出现在数据类型跨字节时,数据类型为单个字节时不存在大小端问题。
二、网络字节序
网络字节序统一为大端序。在网络编程过程中,双方只需要约定好,是按照大端序传输,还是按照小端序传输。若按照大端传输:当主机CPU存储方式为小端时,则需要将跨字节数据类型部分转换成大端序放进发送缓存区;当主机CPU存储方式为大端时,则不需要对跨字节数据类型进行处理,直接放入发送缓存区即可。反之,如果按照小端传输:当主机CPU存储方式为大端时,则需要将跨字节数据类型部分转换成小端序放进发送缓存区;当主机CPU存储方式为小端时,则不需要对跨字节数据类型进行处理,直接放入发送缓存区即可。
三、网络字节序转换函数
根据上述分析,操作系统也提供了一些常用字节序转换函数。
字节序转换函数头文件:
#include
常用的字节序转换函数有:
htons():将短整型(short)数据从主机字节序(大/小端序)转换成网络字节序(大端序);
ntohs():将短整型(short)数据从网络字节序(大端序)转换成主机字节序(大/小端序);
htonl():将长整型(long int)数据从主机字节序(大/小端序)转换成网络字节序(大端序);
ntohl():将长整型(long int)数据从网络字节序(大端序)转换成主机字节序(大/小端序);
从上述分析可知:当主机存储方式为大端序时,调用这些函数时,返回值和输入值相同,即函数不对数据进行转换操作。为让大家更好的明白这些函数的原理,在此,简单实现一遍这些函数。
typedef unsigned short uint16;typedef unsigned int uint32;//短整型大小端互换#define BigLittleSwap16(A) ((((uint16)(A) & 0XFF00) >> 8) | \ (((uint16)(A) & 0X00FF) << 8))//整型大小端互换#define BigLittleSwap32(A) ((((uint32)(A) & 0XFF000000) >> 24) | \ (((uint32)(A) & 0X00FF0000) >> 8) | \ (((uint32)(A) & 0X0000FF00) << 8) | \ (((uint32)(A) & 0X000000FF) << 24))//判断本机CPU字节序,大端返回true,小端返回falsebool checkCPUendian() { union{ unsigned int i; unsigned char s[4]; }c; c.i =0X12345678; return (0X12 == c.s[0]);} /** * 以下函数执行逻辑均如下: * 如果本机字节序为大端,则与网络字节序相同,直接返回结果; * 如果本机字节序为小端,则转换字节序为大端,再返回结果。 *///本机字节序转网络字节序unsigned int t_htonl(unsigned int val) { return checkCPUendian() ? val : BigLittleSwap32(val);}//网络字节序转本机字节序unsigned int t_ntohl(unsigned int val) { return checkCPUendian() ? val : BigLittleSwap32(val);}//本机字节序转换成网络字节序unsigned short t_htons(unsigned short val) { return checkCPUendian() ? val : BigLittleSwap16(val);}//网络字节序转本机字节序unsigned short t_ntohs(unsigned short val) { return checkCPUendian() ? val : BigLittleSwap16(val);}
即在每个函数中,都会调用一下判断本机存储方式的函数,如果本机是大端序,则不对输入进行处理,如果本机为小端序时,则对数据进行变换。
因此,在双方约定为大端传输时,可以调用htons()、ntohs()、htonl()、ntohl()这些函数对数据进行处理。
但是,如果双方约定为小端传输时,则不可以调用这些函数,需要另外实现相关功能的函数,这些函数的主要功能是:当主机为小端序时,不对数据进行处理,直接返回结果;当主机为大端序时,对数据中字节顺序进行旋转。
这是在网络编程中总结的一些心得,分享出来希望对大家有用,如有错误请大家指正!喜欢的小伙伴可关注CSDN博客:weixin_44953262