在這之前,先來理清下大小端存儲的區別,一般用的 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);
// 第二個參數可以傳入一個整數指針