通过域名获取主机IP -- struct addrinfo
参考书籍:《UNIX环境高级编程》 (APUE,男神的书,出第三版了,有需要的私信我)
文章目录
- 结构体定义
- 参数释义:
- ai_flags
- ai_family
- ai_socktype
- ai_protocol
- ai_next
- 相关函数
- getaddrinfo
- 参数释义:
- 返回值
- 栗子
- freeaddrinfo
- 使用示例
- 亲测:
- 参数释义:
- ai_flags
- ai_family
- ai_socktype
- ai_protocol
- ai_next
- getaddrinfo
- 参数释义:
- 返回值
- 栗子
- freeaddrinfo
- 亲测:
结构体定义
addrinfo结构主要在网络编程解析hostname时使用,其在头文件#include<netdb.h>中,定义如下:
struct addrinfo
{
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_canonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};
可以说是新面孔,也可以说是老面孔,那我来介绍一下? 那就介绍一下:好的其实它的介绍已经挺明白了。
跟sin_addr和s_addr差不多。
参数释义:
ai_flags
用来指定如何处理地址和名字,可取得值如下:
就改个前缀,是吧
ai_family
这里直接连前缀都不改了
ai_socktype
同上
ai_protocol
IPPROTO_IP :IP协议
IPPROTO_IPV4 :IPv4
IPPROTO_IPV6 :IPv6
IPPROTO_TCP :TCP
IPPROTO_UDP :UDP
这个改动的东西比较多。
ai_next
由于一个域名可以对应多个IP地址,addrinfo也就支持了这个场景。addrinfo通过链表的方式存储其他地址的,可以遍历其属性ai_next获得。
相关函数
getaddrinfo
找了一圈也找不到它的源码,只能把声明贴出来了,什么时候找着了再补上来。
int getaddrinfo(const char *restrict nodename, /* host 或者IP地址 */
const char *restrict servname, /* 十进制端口号 或者常用服务名称如"ftp"、"http"等 */
const struct addrinfo *restrict hints, /* 获取信息要求设置 */
struct addrinfo **restrict res); /* 获取信息结果 */
参数释义:
nodename: 主机名(“lion-wu.blog.csdn.net”)或者是数字化的地址字符串(IPv4的点分十进制串(“192.168.128.64”)或者IPv6的16进制串)。 如果 ai_flags 中设置了AI_NUMERICHOST 标志,那么该参数只能是数字化的地址字符串,不能是域名,该标志的作用就是阻止进行域名解析。 nodename 和 servname 可以设置为NULL,但是同时只能有一个为NULL。
servname: 服务名可以是十进制的端口号(“8080”)字符串,也可以是已定义的服务名称,如"ftp"、"http"等,详细请查看/etc/services 文件,最后翻译成对应服务的端口号。如果此参数设置为NULL,那么返回的socket地址中的端口号不会被设置。 如果 ai_flags 设置了AI_NUMERICSERV 标志并且该参数未设置为NULL,那么该参数必须是一个指向10进制的端口号字符串,不能设定成服务名,该标志就是用来阻止服务名解析。
hints: 该参数指向用户设定的 struct addrinfo 结构体,只能设定该结构体中 ai_family、ai_socktype、ai_protocol 和 ai_flags 四个域,其他域必须设置为0 或者 NULL, 通常是申请 结构体变量后使用memset()初始化再设定指定的四个域。 该参数可以设置为NULL,等价于 ai_socktype = 0, ai_protocol = 0,ai_family = AF_UNSPEC,ai_flags = 0。
res: 该参数获取一个指向存储结果的 struct addrinfo 结构体列表,使用完成后调用 freeaddrinfo() 释放存储结果空间。
返回值
如果 getaddrinfo() 函数执行成功,返回值为 0 , 其他情况返回值表示错误种别。使用函数gai_strerror() 可以获取可读性的错误信息,用法用strerror()相同。
栗子
ret = getaddrinfo("lion-wu.blog.csdn.net", NULL, &hint, &res);
if (ret != 0)
{
printf("getaddrinfo errorn");
return -1;
}
freeaddrinfo
void freeaddrinfo(struct addrinfo *ai)
{
struct addrinfo *next;
#if defined(__BIONIC__)
if (ai == NULL) return;
#else
_DIAGASSERT(ai != NULL);
#endif
do {
next = ai->ai_next;
if (ai->ai_canonname)
free(ai->ai_canonname);
/* no need to free(ai->ai_addr) */
free(ai);
ai = next;
} while (ai);
}
使用示例
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int ret = -1;
struct addrinfo *res;
struct addrinfo hint;
struct addrinfo *curr;
char ipstr[16];
if (argc != 2) {
printf("parameter errorn");
return -1;
}
bzero(&hint, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
ret = getaddrinfo(argv[1], NULL, &hint, &res);
if (ret != 0)
{
printf("getaddrinfo errorn");
return -1;
}
for (curr = res; curr != NULL; curr = curr->ai_next)
{
inet_ntop(AF_INET,&(((struct sockaddr_in *)(curr->ai_addr))->sin_addr), ipstr, 16);
printf("%sn", ipstr);
}
freeaddrinfo(res);
return 0;
}
亲测:
能猜到为啥第一次操作失败了吗?
今天的技术介绍就到这里啦,我要介绍一位我的新朋友: 唔仄lo咚锵 我们学校软件工程系的大佬,Java、算法、redis领域博主,正在厚积薄发当中。
然后,顺便也可以看看我的其他博客,lion-wu.blog.csdn.net
刷一下就过去了,确定不留下吗?
- java学习之第五章编程题示例(初学篇)
- java第四章编程题(初学篇)
- java测试Unicode编码以及数组的运用(初学篇)
- HDUOJ---1754 Minimum Inversion Number (单点更新之求逆序数)
- HDUOJ-------1753大明A+B(大数之小数加法)
- HDUOJ---1754 I Hate It (线段树之单点更新查区间最大值)
- HDUOJ----1166敌兵布阵(线段树单点更新)
- poj----2155 Matrix(二维树状数组第二类)
- poj------2352 Stars(树状数组)
- HDUOJ-----2852 KiKi's K-Number(树状数组+二分)
- nyoj----522 Interval (简单树状数组)
- HDUOJ-----2838Cow Sorting(组合树状数组)
- HDUOJ---2642Stars(二维树状数组)
- HDUOJ -----Color the ball
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 微信小程序开发实战(28):播放、暂停、停止声音
- 微信小程序开发实战(29):控制背景音乐
- 面试:如何从 100 亿 URL 中找出相同的 URL?
- Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
- 不要再对类别变量进行独热编码了
- 面试Java基础问题汇总
- K8s集群上使用Helm部署2.4.6版本Rancher集群
- 一个工作三年的同事,居然还搞不清深拷贝、浅拷贝...
- 太有意思了,教你实现实现王者荣耀团战!
- 动画:什么是基数排序?
- 一个有意思的分钱模拟问题
- 如何快速的开发一个完整的直播购物源码,基础篇
- 「拥抱开源」Nacos 实战篇
- 仅2M!免费软件又一次干掉了付费版
- python爬虫学习 爬取幽默笑话网站