我们只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位的IP地址。在日常生活中,我们常用点分十进制的字符串来表示IP地址。以下函数可在字符串表示和in_addr表示之间转换。
- 字符串转in_addr的函数
- in_addr转字符串的函数
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口类型为void*。
代码示例:
#include <stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> int main(){ struct sockaddr_in addr; inet_aton("127.0.0.1",&addr.sin_addr); uint32_t* ptr = (uint32_t*)(&addr.sin_addr); printf("addr:%x\n",*ptr); printf("addr_str:%s\n",inet_ntoa(addr.sin_addr)); return 0; }
结果演示:
- inet_ntoa详解
从函数原型我们可以清楚地看到,inet_ntoa函数返回了一个char*,即该函数自己在内部申请了一块内存来保存IP的结果。那么这块内存是否需要调用者手动释放呢?
通过查询man手册,我们可以了解到,inet_ntoa函数是将返回结果放至静态存储区,不需要我们手动释放。当多次调用该函数时,函数结果会发生覆盖现象。
代码示例:
#include <stdio.h> #include<netinet/in.h> #include<arpa/inet.h> int main(){ struct sockaddr_in addr1; struct sockaddr_in addr2; addr1.sin_addr.s_addr = 0; addr2.sin_addr.s_addr = 0xffffffff; char* ptr1 = inet_ntoa(addr1.sin_addr); char* ptr2 = inet_ntoa(addr2.sin_addr); printf("ptr1:%s,ptr2:%s\n",ptr1,ptr2); return 0; }
结果演示:
通过结果我们可以清楚地看到,第二次调用时的结果将第一次调用时的结果覆盖掉了。
在《Unix环境高级编程》一书中,明确提出了inet_ntoa不是线程安全的函数。
多线程调动inet_ntoa代码示例:
#include <stdio.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<pthread.h> void* Func1(void* p){ struct sockaddr_in* addr = (struct sockaddr_in*)p; while(1){ char* ptr = inet_ntoa(addr->sin_addr); printf("addr1:%s\n",ptr); } return; } void* Func2(void* p){ struct sockaddr_in* addr = (struct sockaddr_in*)p; while(1){ char* ptr = inet_ntoa(addr->sin_addr); printf("addr2:%s\n",ptr); } return; } int main(){ pthread_t tid1 = 0; struct sockaddr_in addr1; struct sockaddr_in addr2; addr1.sin_addr.s_addr = 0; addr2.sin_addr.s_addr = 0xffffffff; pthread_create(&tid1,NULL,Func1,&addr1); pthread_t tid2 = 0; pthread_create(&tid2,NULL,Func2,&addr2); pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
结果演示: