inet_aton & inet_ntoa 和 和inet_pton & inet_ntop
1、先了解一下需要用到的数据类型。
typedef uint32_t in_addr_t;
in_addr_t 这个类型保存的数据,到底是按本机字节序保存的,还是网络字节序保存的,这是不确定的!!!
2、为了解决此问题,Linux 又定义了一个新的结构体类型 struct in_addr,它明确的表示,它保存的 ip 地址就是网络字节序的!!!
struct in_addr {
in_addr_t s_addr;
}
如果说你传了一个本机字节序的 unsigned int 类型的整数给 in_addr 的 s_addr 成员,很抱歉,后面使用到该结构体的函数都会出错。
3、套接字地址在程序中的表示
这个结构体的样子如下:
struct sockaddr_in {
sa_family_t sin_family; /* 这个值固定写 AF_INET */
in_port_t sin_port; /* 网络字节序的端口号 */
struct in_addr sin_addr; /* in_addr 类型的 ip 地址 */
};
inet_addr和inet_network函数都是用于将字符串形式转换为整数形式用的,两者区别很小,inet_addr返回的整数形式是网络字节序,而inet_network返回的整数形式是主机字节序。
他俩都有一个小缺陷,那就是当IP是255.255.255.255时,这两个“小子”(对这两个函数的昵称,请谅解…^_^)会认为这是个无效的IP地址,这是历史遗留问题,其实在目前大部分的路由器上,这个255.255.255.255的IP都是有效的。
在Unix网络编程中,我们常用到地址转换函数,它将ASCII字符串(如"206.62.226.33")与网络字节序的二进制值(这个值保存在套接口地址结构中)间进行地址的转换。
1、inet_aton、inet_addr和inet_ntoa在点分十进制数串(例如"206.62.226.33")与它的32位网络字节序二进制值间转换IPv4地址。
2、两个较新的函数:inet_pton和inet_ntop对IPv4和IPv6地址都能进行处理。
1 #include
2
3 /* 返回1:串有效,返回0:串出错 */
4 int inet_aton(const char *strptr, struct in_addr *addrptr);
5
6 /* 若成功,返回32位二进制的网络字节序地址;若出错,返回INADDR_NONE */
7 in_addr_t inet_addr(const char *strptr);
8
9 /* 返回指向点分十进制数串的指针 */
10 char* inet_ntoa(struct in_addr inaddr);
inet_aton将strptr所指的C字符转换为32位网络字节序二进制值,并用一个出参addrptr来存储,返回值为1代表成功,否则返回0。
inet_addr与inet_aton不同在于,他的返回值为转换后的32位网络字节序二进制值,而不是作为出参返回,这样存在一个问题,他的返回值返回的有效IP地址为0.0.0.0到255.255.255.255,如果函数出错,返回常量值INADDR_NONE(这个值一般为一个32位均为1的值),这意味着点分二进制数串255.255.255.255(IPv4的有限广播地址)不能由此函数进行处理。
inet_ntoa将一个32位的网络字节序二进制IPv4地址转换为相应的点分十进制数串。
inet_aton 和inet_ntoa(线程不安全)现在用的比较多,推荐使用。
static void bail(const char *on_what)
{
fputs(on_what,stderr);
fputs("\n",stderr);
}
int main(int argc,char **argv)
{
int z;
struct sockaddr_in adr_inet; /* AF_INET */
int len_inet; /* length */
int sck_inet; /* Socket */
/* Create a Socket */
sck_inet = socket(AF_INET, SOCK_STREAM, 0);
if (sck_inet == -1)
bail("Socket()");
/* Establish address */
memset(&adr_inet, 0, sizeof(adr_inet));
adr_inet.sin_family = AF_INET;
adr_inet.sin_port = htons(9000); .
if( !inet_aton("127.0.0.1", &adr_inet.sin_addr))/*******************
bail("bad address");
len_inet = sizeof(adr_inet);
/* Bind it to the socket */
z = bind(sck_inet, (struct sockaddr *)&adr_inet,len_inet);
if(z == -1)
bail("bind()");
/* Display our socket address */
system("netstat -pa --tcp 2>/dev/null" | grep inetaton");
return 0;
}
程序的运行结果如下:
S$ ./inetaton
tcp 0 0 127.0.0.23:9000 *:* CLOSE 1007/inetaton
inet_pton和inet_ntop两个函数较新,对IPv4和IPv6地址都能进行处理,字母p代表presentation,字母n代表numeric。地址的表达格式通常是ASCII串,数值格式则是存在于套接口地址结构中的二进制值。
1 #include
2
3 /* 若函数成功,则返回1;若输入不是有效的格式,则函数返回0;若处理失败,函数返回-1 */
4 int inet_pton(int family, const char *strptr, void *addrptr);
5
6 /* 若函数处理成功,返回指向结果的指针;若函数处理失败,返回NULL */
7 const char* inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
两个函数的参数family既可以是AF_INET,也可以是AF_INET6。如果以不被支持的地址族作为family参数,两个函数都返回错误,并将errno置为EAFNOSUPPORT。
第一个函数转换由指针strptr所指的串,通过指针addrptr存储二进制结果,如果成功,则返回值为1;如果对于指定的family输入串不是有效的表达格式,则返回值为0.
inet_ntop进行相反的转换,即从数值格式(addrptr)到表达格式(strptr)进行转换。参数len是目标的大小,以免函数溢出其调用者的缓冲区。为有助于规定这个大小,在头文件
1 #define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */
2 #define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */
如果len太小,无法容纳表达格式结果(包括终止的空字符),则返回一个空指针,并置errno为ENOSPC。
函数inet_ntop的参数strptr可不能是个空指针,调用者必须为目标分配内存指定大小。成功时,此指针即函数的返回值。
---------------------------------------------------------------------------------------------
上面的这几个函数,都是在字符串和整数之间转换。
下面这几个函数用的也比较多,是整数之间的转换。
POSIX 提供了 4 个函数(也可能是用宏来实现的),可以让本机字节序和网络字节序之间进行互转。它们分别是:
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
其中,函数名字中的 h 表示 host(本机),n 表示 network(网络),而 l 表示要转换的数据是 4 字节,s 表示要转换的数据是 2 字节。