大端小端
网络字节序采用大端表示,就是数据的高位要存放到低地址。
而大多数主机字节序采用小端表示(也有采用大端表示的主机字节序),就是数据的低位放到低地址。
一、网络字节序和主机字节序
对IP地址结构体SOCKADDR_IN赋值时,常会用到函数:htonl,htons,inet_addr,与之相对应的函数是ntohl,ntohs,inet_ntoa,这些函数与主机字节序和网络字节序之间转换有关。
用IP地址127.0.0.1为例:
第一步 127 . 0 . 0 . 1 把IP地址每一部分转换为8位的二进制数。
第二步 01111111 00000000 00000000 00000001 = 2130706433 (主机字节序)
然后把上面的四部分二进制数从右往左按部分重新排列,那就变为:
第三步 00000001 00000000 00000000 01111111 = 16777343 (网络字节序)
然后解析上面提到的函数作用就简单多了,看以下代码:
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(2130706433);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
先是定义了一个IP地址结构体addrSrv,然后初始化它的IP时addrSrv.sin_addr.S_un.S_addr必须是赋值IP地址的网络字节序,htonl函数的作用是把一个主机字节序转换为网络字节序,也就是上面转换过程中第二步转换为第三步的作用,127.0.0.1的主机字节序是2130706433,把主机字节序2130706433转换为网络字节序就是htonl(2130706433)=16777343,所以如果你知道网络字节序是16777343的话,addrSrv.sin_addr.S_un.S_addr=htonl(2130706433);与addrSrv.sin_addr.S_un.S_addr=16777343;是完全一样的。
addrSrv.sin_addr.S_un.S_addr=htonl(2130706433);这句还可以写为:
addrSrv.sin_addr.S_un.S_addr=inet_addr(“127.0.0.1”); 结果是完全一样的。
可见inet_addr函数的转换作用就是上面的第一步到第三步的转换。
下面再看端口的主机字节序与网络字节序的转换。以6000端口为例。
第一步 00010111 01110000 = 6000 (主机字节序)
端口号其实就已经是主机字节序了,首先要把端口号写为16位的二进制数,分前8位和后8位。
第二步 01110000 00010111 = 28695 (网络字节序)
然后把主机字节序的前八位与后八位调换位置组成新的16位二进制数,这新的16位二进制数就是网络字节序的二进制表示了。
因此,如果你知道6000端口的网络字节序是28695的话。 addrSrv.sin_port=htons(6000);可以直接写为 addrSrv.sin_port=28695;结果是一样的,htons的作用就是把端口号主机字节序转换为网络字节序。
与htonl,htons,inet_addr,与之相对应的函数是ntohl,ntohs,inet_ntoa,不难看出,ntohl,ntohs,inet_ntoa,这三个函数其实就是执行与他们相对应函数的相反转换,在这里就不详细解析了。
htonl --本地地址字节序转化为网络字节序
https://www.cnblogs.com/lanlianggui/p/6821135.html
htons–本地端口字节序转化为网络字节序
inet_addr–本地地址(str)转化为网络字节序
ntohl–网络地址字节序 转化为 本地字节序
ntohs–网络端口字节序 转化为 本地字节序
inet_ntoa–网络字节序 转化为 本地地址(str)
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
int main()
{
char ip[] = "192.168.0.74";
long r1, r2, r3; //long
struct in_addr addr;
r1 = inet_addr(ip); //返回网络字节序
if(-1 == r1){
printf("inet_addr return -1\n");
}else{
printf("inet_addr get network ip1: %ld\n", r1);
printf("inet_addr get network ip1: %0x\n", r1);
}
r2 = inet_network(ip); //返回主机字节序
if(-1 == r2){
printf("inet_network return -1/n");
}else{
printf("inet_network get host ip2: %ld\n", r2);
printf("inet_network get host ip2: %0x\n", r2);
printf("htonl(inet_network) get network ip3: %0x\n", htonl(r2)); //htonl: 主机字节序 ——> 网络字节序
printf("htonl(inet_network) get network ip3: %ld\n", htonl(r2));
}
r3 = inet_aton(ip, &addr); //返回网络字节序
if(0 == r3){
printf("inet_aton return -1\n");
}else{
printf("inet_aton get network ip: %ld\n", addr.s_addr);
printf("inet_aton get network ip: %0x\n", addr.s_addr);
printf("inet_aton and ntohl get host ip: %0x\n", ntohl(addr.s_addr));
}
return 0;
}
IP字符串和网络字节流
int inet_aton(const char *cp, struct in_addr inp);
inet_aton() 转换网络主机地址ip(如192.168.1.10)为二进制数值,并存储在struct in_addr结构中,即第二个参数inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。(这个转换完后不能用于网络传输,还需要调用htons或htonl函数才能将主机字节顺序转化为网络字节顺序)
in_addr_t inet_addr(const char *cp);
inet_addr函数转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp无效,函数返回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回-1,255.255.255.255是一个有效的地址,不过inet_addr无法处理;
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
inet_ntoa 函数转换网络字节排序的地址为标准的ASCII以点分开的地址,该函数返回指向点分开的字符串地址(如192.168.1.10)的指针,该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖),所以如果需要保存该串最后复制出来自己管理!
#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
IPv6地址压缩
参考:
https://blog.csdn.net/ithomer/article/details/6061528
https://www.cnblogs.com/lanlianggui/p/6821135.html