⽹络通信之⼤⼩端模式及字节序转换函数解析
前⾔
在⽹络编程中,我们经常会遇到不同主机间通信时,由于主机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 <arpa/inet.h>
常⽤的字节序转换函数有:
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,⼩端返回false
bool 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);
网络编程之delphi}
//本机字节序转换成⽹络字节序
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()这些函数对数据进⾏处理。
但是,如果双⽅约定为⼩端传输时,则不可以调⽤这些函数,需要另外实现相关功能的函数,这些函数的主要功能是:当主机为⼩端序时,不对数据进⾏处理,直接返回结果;当主机为⼤端序时,对数据中字节顺序进⾏旋转。
这是在⽹络编程中总结的⼀些⼼得,分享出来希望对⼤家有⽤,如有错误请⼤家指正。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论