主机字节序(host-byte)指的是处理器存储数据的字节顺序。对于Inter x86处理器来说,将数据的不重要的部分保存在低地址,重要的部分保存在高地址,即低地址中保存的是数据的低字节位,高地址保存的是数据的高字节位。
int ip_Address_hostbyte = 0x12345678;
此时,Inter x86处理器按照如图1的格式来保存变量ip_Address_hostbyte。
图1 ip_Address_hostbyte的保存格式
从图1中可以看出,变量ip_Address_hostbyte是按照主机字节序的方式被保存的。如果将低地址看成是“头”,主机字节序又叫做“小头”(little-endian)。
网络字节序(network-byte)指的是网络编程时,存储数据的字节顺序。与“主机字节序”相反,网络字节序将数据的重要部分保存在低地址,不重要的部分保存到高地址,即低地址保存的是数据的高字节位,高地址保存的是数据的低字节位。在网络编程时用到的IP地址和端口号都需要网络字节序。
可以通过一系列的函数实现主机字节序和网络字节序之间的转换。
可以通过htonl()、htons()、WSAHtonl()和WSAHtons()函数实现主机字节序转换成网络字节序。函数中的h表示主机host,n表示网络networ,l表示长整形long,s表示短整形short。
htonl()函数的格式为
u_long htonl(u_long hostlong);
其中,参数hostlong是主机字节序的数据,该函数的返回值是转换之后的网络字节序的数据。
int ip_Address_networkbyte = htonl(ip_Address_hostbyte);
其中,ip_Address_hostbyte是“1 主机字节序”中定义的变量。转换后的网络字节序变量ip_Address_network的数据保存格式如图2所示。
图2 ip_Address_network的保存格式
从图2中可以看出,如果将低地址看成“头”的话,网络字节序又可以叫做“大头”(big-endian)。
htons()函数的格式为
u_short htons(u_short hostshort);
该函数的用法与htonl()类似。
WSAHtonl()/WSANtohl()是Winsock版本的转换函数。
WSAHtonl()函数的格式为
int WSAHtonl(SOCKET s, u_long hostLong, u_long* LpnetLong);
其中,参数s是套接字的描述符;hostLong是要转换的主机字节序的长整形;LpnetLong是指针,该指针指向的内容是转换后的网络字节序。如果转换成功,该WSAHtonl()函数返回值是0,否则返回值是SOCKET_ERROR。
在使用Winsock函数进行网络编程时,可以使用以上两个函数。
可以通过ntohl()、ntohs()、WSANtohl()和WSANtohs()函数实现主机字节序转换成网络字节序。使用的方法与“3.1 主机字节序转换成网络字节序”中介绍的相应的四个函数类似。
SOCKADDR_IN结构用来表示网络地址和端口。
该结构的定义为
typedef struct sockaddr_in
{
short sin_family
; ADDRESS_FAMILY sin_family
; USHORT sin_port
; IN_ADDR sin_addr
; CHAR sin_zero[8];
}SOCKADDR_IN, *PSOCKADDR_IN;
其中,sin_family表示地址族;sin_port是通信的端口号,该端口号是网络字节序;sin_addr是IN_ADDR结构的对象,表示IP地址,该IP地址也是网络字节序;sin_zero是字符数组,为了使得SOCKADDR_IN结构与SOCKADDR的结构的大小相同,用sin_zero来作为SOCKADDR_IN结构的填充。
在“4.1 SOCKADDR_IN结构”中提到了该结构的成员变量sin_addr是IN_ADDR结构的对象。IN_ADDR结构的定义为
typedef struct in_addr {
union {
struct {
UCHAR s_b1;
UCHAR s_b2;
UCHAR s_b3;
UCHAR s_b4;
} S_un_b;
struct {
USHORT s_w1;
USHORT s_w2;
} S_un_w;
ULONG S_addr;
} S_un;
} IN_ADDR, *PIN_ADDR, *LPIN_ADDR;
从ADDR_IN结构的定义可以看出,该结构是一个union,包含了IP地址的三种表达方式。其中S_un_b的四个成员变量分别表示点格式IP地址的四个部分;S_un_w的两个成员变量分别表示IP地址的高字部分和低字部分;S_addr是IP地址的无符号长整形格式。
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr.S_un.S_addr);
addr.sin_port = htons(12345);
AF_INET表示使用的IP族;inet_pton()函数的作用是将点格式的IP地址转换为数字,该函数的第3个参数是SOCKADDR_IN结构的S_addr,即无符号长整形,inet_pton()函数的使用方法请参考《VS2015中IP地址转换函数》;通过htons()函数将主机字节序的端口转换为网络字节序,并将其保存在SOCKADDR_IN结构的sin_port中。图3是addr.sin_port的值。
图3 addr.sin_port的值
因为12345的主机字节序是0x3039,所以其网络字节序是0x3930。
相关链接:使用inet_pton()函数将IP地址格式进行转换时,还可以使用如下代码
inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr.s_addr);
因为在inaddr.h中对s_addr进行了定义
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */