字节序转换函数

计算机中读取数据是从低地址往高地址读取的

计算机中多字节整数序列的存储有2种方式:

  1. 小端存储(小端字节序):低序字节存储在低地址
  2. 大端存储(大端字节序):高序字节存储在低地址

主机字节序(Host Byte Order):不同主机存储方式不同,可能大端存储也可能小端存储。

网络字节序(Network Byte Order):网络字节序是大端字节序。

htonl | htons

(h)主机字节序 ——> (n)网络字节序

头文件:
    #include <arpa/inet.h>(有些系统采用#include <netinet/in.h>)
原型:
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
参数:
    uint32_t hostlong:32位主机字节序(4字节);
    uint16_t hostshort:16位主机字节序(2字节);
返回值:
    返回转换后的网络字节序;
例程:
    //端口号宏定义
    #define PORT 8888
    htons(PORT);
    //端口号局部定义
    unsigned short port = 8888;
    htons(port);
    //外部传参方式获取端口号
    htons(*(unsigned short*)argv[2]);//也可以用atoi函数转换类型

ntohl | ntohs

(n)网络字节序 ——> (h)主机字节序

头文件:
    #include <arpa/inet.h>
原型:
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);
参数:
    uint32_t hostlong:32位网络字节序(4字节);
    uint16_t hostshort:16位网络字节序(2字节);
返回值:
    返回转换后的主机字节序;
例程:
    ntohs(caddr.sin_port);//获取与客户端通信的端口号

IP地址(二进制、点分十进制转换)

IP地址在网络字节序中需以二进制传输

点分十进制 ——> 二进制(网络字节序)

inet_aton

只适用于IPv4

a:表示ascii串;n:表示numeric数值

头文件:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
原型:
    int inet_aton(const char *cp, struct in_addr *inp);
参数:
    char *cp:源IP字符串,如 “192.168.1.104”;
    struct in_addr *inp:存储struct in_addr的指针;
        typedef uint32_t in_addr_t;
        struct in_addr {
            in_addr_t s_addr;//IP地址的网络字节序值
        };
返回值:
    成功,返回1;
    失败,返回0,不更新errno;
例程:
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(*(unsigned short*)argv[2]);
    inet_aton(argv[1],&saddr.sin_addr);
    //inet_pton(AF_INET,argv[1],&saddr.sin_addr.s_addr);
    //saddr.sin_addr.s_addr = inet_addr(argv[1]);

inet_pton

可用于IPv4和IPv6

p表示:地址的表达presentation

头文件:
    #include <arpa/inet.h>
原型:
    int inet_pton(int af, const char *src, void *dst);
参数:
    int af:协议族
        AF_INET :IPv4
        AF_INET6:IPv6
    char *src:源IP字符串的地址 “192.168.1.104”;
    void *dst:存储in_addr_t的指针
        int af是AF_INET
            typedef uint32_t in_addr_t;
            struct in_addr{
                in_addr_t s_addr;
            };
        int af是AF_INET6
            struct in6_addr;
返回值:
    成功,返回1;
    af字符串格式错误,返回0;
    出错,返回-1,更新errno;
例程:
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(*(unsigned short*)argv[2]);
    //inet_aton(argv[1],&saddr.sin_addr);
    inet_pton(AF_INET,argv[1],&saddr.sin_addr.s_addr);
    //saddr.sin_addr.s_addr = inet_addr(argv[1]);  

inet_addr

只适用于IPv4

不能转换255.255.255.255;返回值为-1,与错误时返回-1重合了,无法区分

头文件:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
原型:
    in_addr_t inet_addr(const char *cp);
参数:
    char *cp:源IP字符串的地址 “192.168.1.104”;
返回值:
    成功,返回转换后的网络字节序ip:in_addr_t 
        typedef uint32_t in_addr_t;
        struct in_addr {
            in_addr_t s_addr;
        };
    失败,返回-1;
例程:
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(*(unsigned short*)argv[2]);
    //inet_aton(argv[1],&saddr.sin_addr);
    //inet_pton(AF_INET,argv[1],&saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = inet_addr(argv[1]);

二进制 ——> 点分十进制

inet_ntoa

只适用于IPv4

转换后的字符串是存储在静态存储区,下次调用该函数,会把上次的数据覆盖。所以最好保存函数返回的数据;

头文件:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
原型:
    char *inet_ntoa(struct in_addr in);
参数:
    struct in_addr in:存储网络字节序的结构体;
        typedef uint32_t in_addr_t;
        struct in_addr {
            in_addr_t s_addr;
        };
返回值:
    成功,返回点分十进制的字符串的首地址;
    失败,返回NULL;
例程:
    printf("收到来自[%s]客户端的请求信息",inet_ntoa(caddr.sin_addr));
    //char dst[N] = "";
    //printf("收到来自[%s]客户端的请求信息",inet_ntop(AF_INET,&caddr.sin_addr.s_addr,dst,N));

inet_ntop

可用于IPv4和IPv6

头文件:
    #include <arpa/inet.h>
参数:
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
    int af:协议族
        AF_INET :IPv4
        AF_INET6:IPv6
    void *src:存储着in_addr_t的指针
        int af是AF_INET
            typedef uint32_t in_addr_t;
            struct in_addr{
                in_addr_t s_addr;
            };
        int af是AF_INET6
            struct in6_addr;
    char *dst:存储转换后的点分十进制字符串;
    socklen_t size:缓冲区大小,其实就是指定多大的空间用于转换ip;
返回值:
    成功,返回字符串的首地址,就是dst的首地址;
    失败,返回NULL,更新errno;  
例程:
    //printf("收到来自[%s]客户端的请求信息",inet_ntoa(caddr.sin_addr));
    char dst[N] = "";
    printf("收到来自[%s]客户端的请求信息",inet_ntop(AF_INET,&caddr.sin_addr.s_addr,dst,N));

域名转换(域名 ——> IP地址)

gethostbyname

头文件: 
    #include <netdb.h>
原型:
    struct hostent *gethostbyname(const char *name);
参数:
    char *name:域名字符串首地址
返回值:
    成功,返回结构体struct hostent *:
    struct hostent {
        char  *h_name;            /* official name of host */正式主机名
        char **h_aliases;         /* alias list */主机别名
        int    h_addrtype;        /* host address type */主机IP地址族类型
        int    h_length;          /* length of address */主机IP地址长度
        char **h_addr_list;       /* list of addresses */主机绑定的IP地址列表
    };
    出错,返回NULL,设置h_errno;
例程:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(int argc, const char *argv[])
{
    if(argc < 2)
    {
        printf("请输入域名\n");
        return 0;
    }
    struct hostent *host = gethostbyname(argv[1]);
    if(host==NULL)
    {
        printf("获取IP地址失败,result: %s\n",hstrerror(h_errno));
        return -1;
    }
    int i;
    //官方域名
    printf("Official: %s\n",host->h_name);
    //别名
    for(i=0; host->h_aliases[i]; i++){
        printf("Aliases %d: %s\n", i+1, host->h_aliases[i]);
    }
    //地址类型
    printf("Address type: %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
    //IP地址
    for(i=0; host->h_addr_list[i]; i++){
        printf("IP addr %d: %s\n", i+1, inet_ntoa( *(struct in_addr*)host->h_addr_list[i] ) );
    }
    return 0;
}
结果:
$ ./a.out shengto.top
Official: shengto.top
Address type: AF_INET
IP addr 1: 121.199.62.148

$ ./a.out video2020.shengto.top
Official: video2020.shengto.top.w.kunlungr.com    //CDN服务
Aliases 1: video2020.shengto.top        //访问主机别名,其实真正访问的是正式主机名
Address type: AF_INET
IP addr 1: 114.80.187.72
Last modification:2021 年 04 月 17 日 14 : 11 : 26