C++ socket IP int to string 自己实现 inet_ntop

在这之前,先来理清下大小端存储的区别,一般用的 intel x86 架构都是小端的机器。小端指低地址存放低位数据,高地址存放高位数据;而大端反之,低地址存放高位数据。

下面的例子一目了然。

1. 小端 🌰 "192.168.1.1" 字符串转成整型为 

[            1           ]             0x7ffeefbff493

[            1           ]             0x7ffeefbff492

[          168        ]             0x7ffeefbff491

[          192        ]             0x7ffeefbff490

16885952 (unsigned int)

 

2. 大端 🌰 "192.168.1.1" 字符串转成整型为 

[          192        ]             0x7ffeefbff493

[          168        ]             0x7ffeefbff492

[            1           ]             0x7ffeefbff491

[            1           ]             0x7ffeefbff490

= 3232235777 (unsigned int)

⚠️ TCP/IP 协议约定先到达的数据当作高地址来存放,而 socket 字节流发送出去是先送出低地址数据的,这就意味着我们低地址需要存放高位的数据,也就是大端的形式。所以即使转换成字节流,也需要系统提供的 htonl 将小端顺序化成大端。

 

对于 IP 整数值转换成字符串,如果我们确定是小端形式的整形数,我们也可以自己来写转换方法。

#include <netinet/in.h>
#include <iostream>

using namespace std;

/**
小端 ip
*/
void ip_int_to_string(uint32_t ip)
{
    char buf[INET_ADDRSTRLEN]; // INET_ADDRSTRLEN = 16
    
    char n_buf[5];
    for (size_t i = 0; i < 4; ++i)
    {
        char ch = (i == 3 ? '\0' : '.');
        int net = *((uint8_t*)(&ip) + i); // get each byte of ip
        snprintf(n_buf, sizeof(n_buf), "%d%c", net, ch);
        strcat(buf, n_buf);
    }
    cout << buf << endl;
}


/**
大端 ip
*/
void ip_int_to_string(uint32_t ip)
{
    char buf[INET_ADDRSTRLEN]; // INET_ADDRSTRLEN = 16
    
    char n_buf[5];
    for (size_t i = 3; i <= 3; --i)
    {
        char ch = (i ? '.' : '\0');
        int net = *((uint8_t*)(&ip) + i); // get each byte of ip
        snprintf(n_buf, sizeof(n_buf), "%d%c", net, ch);
        strcat(buf, n_buf);
    }
    cout << buf << endl;
}

 

# 后续

inet_pton 和 inet_ntop 是一组用于 ipv4 网络地址格式转换的方法。被包含在 #include <arpa/inet.h> 头文件中。

inet_pton 负责将 cost char * 的 IP 字符串转换为整数,inet_ntop 将 IP 整数值转换为字符串。

 

int inet_pton(int, const char *, void *);

const char *inet_ntop(int, const void *, char *, socklen_t);


int main()
{
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr)); // clear with 0
    inet_pton(AF_INET, "192.168.1.1", &(addr.sin_addr)); // "192.168.1.1" => addr.sin_addr
    
    cout << addr.sin_addr.s_addr << endl; // 16885952
    
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN);
    cout << ip << endl; // 192.168.1.1
}

struct sockaddr_in 被包含在 #include <netinet/in.h> 头文件中,结构体定义如下

/*
 * Socket address, internet style.
 */
struct sockaddr_in {
	__uint8_t       sin_len;
	sa_family_t     sin_family;
	in_port_t       sin_port;
	struct  in_addr sin_addr;
	char            sin_zero[8];
};

/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
	in_addr_t s_addr;
};


typedef __uint32_t      in_addr_t;      /* base type for internet address */

前面提到的 IP 整数值存放在 sin_addr 里,而它是 in_addr 结构体,包含一个成员 in_addr_t s_addr,而 in_addr_t 就是一个 uint32_t 无符号 int 类型。

inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN);
// 也可以写成
inet_ntop(AF_INET, &(addr.sin_addr.s_addr), ip, INET_ADDRSTRLEN);
// 第二个参数可以传入一个整数指针

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章