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);
// 第二個參數可以傳入一個整數指針

 

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