字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。
基于X86平台的PC机是小端字节序的,而有的嵌入式平台则是大端字节序的。因而对int、uint16、uint32等多于1字节类型的数据,在这些嵌入式平台上应该变换其存储顺序。通常我们认为,在空中传输的字节的顺序即网络字节序为标准顺序,考虑到与协议的一致以及与同类其它平台产品的互通,在程序中发数据包时,将主机字节序转换为网络字节序,收数据包处将网络字节序转换为主机字节序。
无论采用的是大端顺序还是小端顺序,在网络通信中,对一台计算机所采用的字节顺序都统称为主机字节顺序。当不同字节顺序的计算机在通过网络交换数据时,如果不做任何处理,将会出现严重问题。为了解决这一问题,在编写网络程序的时候,规定发送端要发送的多字节数据必须先转换成与具体CPU无关的网络字节顺序再发送,接收端接收到数据后再将数据转换为主机字节数据。网络字节顺序采用的是大端存储方式。
1.htons函数
原型:u_short htons(u_short hostshort);
功能:该函数将一个16位的无符号短整型数据由主机字节顺序转换为网络字节顺序。
2.ntohs函数
原型:u_short ntohs(u_short netshort);
功能:该函数将一个16位的无符号短整型数据由网络字节顺序转换为主机字节顺序。
3.htonl函数
原型:u_long htonl(u_long hostlong);
功能:该函数将一个32位的无符号长整型数据由主机字节顺序转换为网络字节顺序。
4.ntohl函数
原型:u_long ntohl(u_long netlong);
功能:该函数将一个32位的无符号长整型数据由网络字节顺序转换为主机字节顺序。
程序例子:主机字节顺序到网络字节顺序转换函数的使用
#include "WinSock2.h" //包含WinSock库头文件
#include <iostream>
#pragma comment(lib,"ws2_32.lib") //链接WinSock导入库
using namespace std;
int main(){
//加载WinSock DLL
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(2,2);
if(WSAStartup(wVersionRequested,&wsaData)!=0){
cout<<"加载WinSock DLL失败!"<<endl;
return 0;
}
u_short x,y=0x1234;
x=htons(y); //将y值转换为网络字节顺序并将转换结果存入变量x
cout<<"主机字节顺序:"<<hex<<y<<" "<<"网络字节顺序"<<x<<endl;
u_long a,b=0x1122ABCD;
a=htonl(b); //将b值转换为网络字节顺序并将转换结果存入变量a
cout<<"主机字节顺序:"<<hex<<b<<" "<<"网络字节顺序"<<a<<endl;
WSACleanup(); //注销WinSock DLL
system("pause");
}
针对上面4个函数,WinSock2提供了扩展形式,分别是:WSAHtons,WSANtohs,WSAHtonl,WSANtohl。以WSAHtons为例:
原型:int WSAHtons(SOCKET s,u_short hostshort,u_short * lpnetshort);
功能:该函数将一个16位的无符号短整型数据由主机字节顺序转换为网络字节顺序。
参数: s:套接字描述符 hostshort:一个待转换的主机字节顺序的16位无符号短整型数据 lpnetshort:指向一个16位无符号短整型变量指针,该指针用于存储转换后的网络字节顺序16位数据。
剩下的函数类似,以上所有函数在调用失败后返回SOCKET_ERROR,错误信息可调用WSAGetLastError()获取。
如何手算主机字节顺序转换为网络字节顺序?
假设某16位的整数,主机字节顺序的值是21,那么它的网络字节顺序是多少?
解决的步骤是:
1、将21化成二进制,二进制,如果不足16位就在其前面补0,补满16位。
21转换成二进制是:10101,在它前面补0,补满16位后就得到:
00000000#00010101
2、将这个16位二进制字符平分成两段,每段8位
0000000 000010101 == > 00000000#00010101
3、颠倒这两段的顺序,然后去掉第一个字符“1”前面的0,化成十进制就得到了网络字节顺序的值了。
00000000#00010101颠倒后:00010101#00000000
即00 01 01 01 00 000000
去掉第一个“1”前面的0得到:10 10 10 00 00 00 0 化成十进制得到:5376
如果IP地址使用的是32位的无符号整数,所以,在对IP地址进行处理的时候,就需要用到32位的转换了。例如一个点分十进制的IP地址是192.168.0.1,还原成原来的二进制原码是: 11000000 10101000 0000000 00000 0001 这是主机字符顺序存储的值。将其按照每个字节分隔开来,即每8位分隔开来,得到:
11000000 10101000 00000000 00000001 将这4段二进制编码进行完全颠倒,就得到了网络字节顺序:
00000001000000001010100011000000 去掉第一个字符“1”前面无效的0,得到:
10 00 00 00 01 01 01 00 01 10 00 00 0
化成十进制得到:16820416