目录
inet_addr 、inet_aton 和 inet_ntoa
主机字节序和网络字节序
现代 CPU 的累加器一次能装载至少 4 字节,即一个整数。那么这 4个 字节在内存中的排列的顺序将影响它被累加器装载成的整数的值,这就是字节序问题。字节序分为大端字节序 (即大端模式,Big endian) 和 小端字节序 (即小端模式,Little endian)。
大端字节序:整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
小端字节序:整数的高位字节存储在内存的高地址处,而低位字节存储在内存的低地址处。
以下两种方法可以用来验证你的 PC 是大端模式还是小端模式:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int as = 0x12345678;
char *p =(char *) &as;
if(*p == 0x12)
cout << "Big-endian" <<endl;
else
cout << "Little-endian" <<endl;
union {
short s;
char c[sizeof(short)];
} un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("Big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("Little-endian\n");
else
printf("unknown\n");
} else
printf("sizeof(short) = %d\n", sizeof(short));
return 0;
}
一个 32 位值的、4 字节以下顺序传输:首先是0 - 7位,然后是8 - 15位,接着是16 - 23位,最后是24 - 31位。这就是所谓的大端字节序,它是 TCP/IP 头部中所有二进制整数在网络中传输时所需的字节顺序,它也被称为网络字节序。计算机的 CPU 使用其他格式存储二进制整数,例如大多数 PC 使用小端字节序。
当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端按自己模式对数据进行提取或解释时必然会导致错误。解决问题的方法是:发送端总是把要发送的数据转化成大端模式数据后再发送,而接收段知道对方传送过来的数据总是采用大端模式,所以接收段可以根据自身采用的模式决定是否对接收到的数据进行转换。
注意:网络字节序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,它保证数据在不同主机之间传输时能够被正确解释。
转换函数
htonl 、htons 、ntohl 和 ntohs
Linux 提供了如下4个函数来完成主机字节序和网络字节序之间的转换:
头文件#include <netinet/in.h>
unsigned long int htonl ( unsigned long int hostlong );
htonl 把unsigned long int类型从主机序转换到网络序
unsigned short int htons ( unsigned short int hostlong );
htons把unsigned short int类型从主机序转换到网络序
unsigned long int ntohl ( unsigned long int netlong );
ntohl 把unsigned long int类型从网络序转换到主机序
unsigned short int ntohs ( unsigned short int netshort );
ntohs 把unsigned short int类型从网络序转换到主机序
在这 4 个函数中,长整型函数通常用来转换 IP 地址,短整型函数用来转换端口号。当然函数的使用不限于此,任何格式化的数据通过网络传输时,都应该使用这些函数来转换字节序。
inet_addr 、inet_aton 和 inet_ntoa
下面 3 个函数可用于将点分十进制字符串表示的 IPv4 地址和用网络字节序整数表示的 IPv4 地址之间进行转换,这里做一个简单的介绍:
#include<arpa/inet.h>
in_addr_t inet_addr( const char *strptr );
int inet_aton( const char * cp, struct in_addr* inp );
char * inet_ntoa( struct in_addr in );
inet_addr 函数将用点分十进制字符串表示的 IPv4 地址转化为用网络字节序整数表示的 IPv4 地址。它失败时返回 INADDR_NONE。
inet_aton 函数完成和 inet_addr 同样的功能,但是将转化结果存储于参数 inp 指向的地址结构中。它成功返回 1 ,失败返回 0。
inet_ntoa 函数将用网络字节序整数表示的 IPv4 地址转化为用点分十进制字符串表示的 IPv4 地址。但需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此 inet_ntoa 是不可重入的。
inet_pton 和 inet_ntop
inet_pton 和 inet_ntop也能完成和前面 3 个函数同样的功能,并且它们同时适用于 IPv4 地址和 IPv6 地址。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
inet_pton 函数将用字符串表示的 IP 地址 src 转换成用网络字节序整数表示的 IP 地址,即[点分十进制] -> [二进制整数]。并把转换结果存储于 dst 指向的内存中。
参数1:af 指定地址族,可以是 AF_INET 或 AF_INET6。
参数2:src 是来源地址。
参数3:dst 接收转换后的数据。
若成功,则返回1;如果函数出错将返回一个负值,并将 errno 设置为 EAFNOSUPPORT;如果参数 af 指定的地址族和 src 格式不对,函数将返回 0。
#include <sys/socket.h>
#include <netinet/in.h>
#include<arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, size_t len);
这 inet_ntop 函数将[二进制整数] -> [点分十进制]。
参数1:af 可以是 AF_INET 或 AF_INET6。
参数2:src 是一个指向网络字节序的二进制值的指针。
参数3:dst 是一个指向转换后的点分十进制串的指针。
参数4:len 是目标的大小,以免函数溢出其调用者的缓冲区。
若成功,则为指向结果的指针;若出错,则返回 NULL。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main ()
{
char IPdotdec[20]; //存放点分十进制IP地址
struct in_addr s; //IPv4地址结构体
//输入IP地址
printf("Please input IP address: ");
scanf("%s", IPdotdec);
//转换
inet_pton(AF_INET, IPdotdec, (void *)&s);
printf("inet_pton: 0x%x\n", s.s_addr); //注意得到的字节序
//反转换
inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);
printf("inet_ntop: %s\n", IPdotdec);
}
输出:
Please input IP address: 192.169.1.1
inet_pton: 0x101a9c0
inet_ntop: 192.169.1.1
参考:
百度百科:https://baike.baidu.com/item/inet_pton
https://blog.csdn.net/u010889616/article/details/47157637?utm_source=blogxgwz6