在这之前,先来理清下大小端存储的区别,一般用的 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);
// 第二个参数可以传入一个整数指针