//libevent笔记-dns解析
//转载请注明出处: yuliying的csdn博客.
//libevent提供dns解析功能,包括阻塞和非阻塞两种API.
//阻塞API类似于平常getaddrinfo() 函数的使用.
//这里只记录异步API的dns客户端部分.毕竟平常阻塞api和dns服务端用不着.
//=============================evutil_addrinfo结构===========================================
//evutil_addrinfo结构的作用有两个: ①用来返回域名解析的结果. ②作为参数用来过滤域名解析结果,只返回符合条件的结果.
/* 和系统头文件提供的addrinfo结构基本一样:
struct evutil_addrinfo {
int ai_flags; // 指定如何来处理地址和名字
int ai_family; // 地址类型 : AF_INET,AF_INET6,AF_UNIX etc
int ai_socktype; // 套接字类型 SOCK_STREAM,SOCK_DATAGRAM,SOCK_RAW
int ai_protocol; // 协议 IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc
size_t ai_addrlen; // 地址长度
char *ai_canonname; // 主机规范名
struct sockaddr *ai_addr; // 地址
struct evutil_addrinfo *ai_next; // 链表中的下一个结构
};
*/
//=====ai_flags 用来指定如何处理地址和名字,可用标志有:
//AI_ADDRCONFIG :查询配置的地址类型(IPv4或IPv6)。
//AI_ALL :查找IPv4和IPv6地址,仅用于 AI_V4MAPPED 。
//AI_CANONNAME :需要一个规范名而不是别名。
//AI_NUMERICHOST :以数字格式返回主机地址。
//AI_NUMERICSERV :以端口号返回服务。
//AI_PASSIVE :套接字地址用于监听绑定。
//AI_V4MAPPED :如果没找到IPv6地址,返回映射到IPv6格式的IPv4地址。
//======解析域名时可以使用hints参数来过滤返回的地址,它是一个evutil_addrinfo结构.
//它只使用 ai_flags、ai_family、ai_socktype、ai_protocol字段其他字段必须设为0或 NULL.
//======释放evutil_addrinfo结构
// void evutil_freeaddrinfo(struct evutil_addrinfo *ai);
//=============================evdns_base结构================================================
//evdns_base结构用来存储域名解析服务器地址和解析的配置.使用 evdns_base_new() 函数来创建.
//函数原型如下:
//struct evdns_base *evdns_base_new(struct event_base *event_base, int initialize);
//失败时返回NULL.
//如果initialize参数设置为1,则使用操作系统的默认配置.如果为0,则不设置域名解析服务器和配置参数.
//可以使用evdns_base_resolv_conf_parse()函数来读取一个配置文件,实现自定义配置,这里就不多说了.
//以下函数用来配置一个evdns_base:
//===========添加一个域名服务器(使用sockaddr结构):
// int evdns_base_nameserver_sockaddr_add(struct evdns_base *base,const struct sockaddr *sa, ev_socklen_t len , unsigned flags);
//flag参数暂时没用,设置为0. 成功返回0,失败返回负数.
//===========添加一个域名服务器(使用ip地址):
// int evdns_base_nameserver_ip_add(struct evdns_base *base , const char *ip_as_string);
//成功返回0,失败返回负数. ip地址格式为"IPv4:Port" 或者 "[IPv6]:Port"
//===========添加域名服务器(使用host文件) :
// int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname);
//成功返回0,失败返回负数.
//===========使用 evdns_base_free()函数来释放 evdns_base结构.
// void evdns_base_free(struct evdns_base *base, int fail_requests);
//如果fail_requests参数为true, 会调用所有未完成任务的回调函数,并且错误码为取消任务.
//=============================异步解析回调函数=============================================
//回调函数的原型是:
// typedef void (*evdns_getaddrinfo_cb)(int result, struct evutil_addrinfo *res, void *arg);
//result为0表示成功,具体错误码参照手册.
//可以使用 const char *evutil_gai_strerror(int err); 函数来把error 信息打印出来.
//===========================使用evdns_getaddrinfo()函数来异步解析dns=======================
//函数原型如下:
//struct evdns_getaddrinfo_request *evdns_getaddrinfo( struct evdns_base *dns_base , const char *nodename,
// const char *servname, const struct evutil_addrinfo *hints ,evdns_getaddrinfo_cb cb, void *arg);
//nodename 参数和 servname 参数需要至少一个不为NULL.
//nodename 参数的格式为: ipv4地址("127.0.0.1") 或者 ipv6地址("::1") 或者 网址("www.example.com")
//servname 参数的格式为: 协议名称("https") 或者 端口号("443")
//hints 参数为过滤选项.
//cb参数为回调函数.
//arg参数为用户自定义参数,作为回调函数的参数.
//如果 evdns_getaddrinfo 失败或者立刻成功,返回NULL. 否则返回一个 evdns_getaddrinfo_request 结构体.
//我们使用evdns_getaddrinfo_request结构体和evdns_getaddrinfo_cancel()函数可以用来取消解析.
//cb 回调函数无论如何都会被调用,即使是取消了解析.
//当evdns_getaddrinfo() 函数被调用时,内部保存了nodename,servname和hints参数的拷贝,如果是动态分配的内存我们可以在调用后回收.
#include <event2/event.h>
#include <event2/util.h>
#include <event2/dns.h>
#include <string.h>
#include <stdlib.h>
struct event_base *base = NULL;
void callback(int errcode, struct evutil_addrinfo *addr, void *ptr){
if (errcode) {
printf("error : %s\n" , evutil_gai_strerror(errcode) );
} else {
struct evutil_addrinfo *ai;
if (addr->ai_canonname){
printf( "cannoname:[%s]\n" , addr->ai_canonname);
}
//addr是一个链表,遍历链表
for( ai = addr ; ai ; ai = ai->ai_next){
char buf[128];
const char *s = NULL;
if ( ai->ai_family == AF_INET){
struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
} else if ( ai->ai_family == AF_INET6){
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
}
if (s){
printf(" ->%s\n" , s);
}
}
evutil_freeaddrinfo(addr);
}
}
int main(){
//event_base是必须的.
base = event_base_new();
//使用系统默认配置
struct evdns_base *dnsbase = evdns_base_new(base, 1);
//用来过滤返回的地址信息.
struct evutil_addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC ; //不指定.
hints.ai_flags = EVUTIL_AI_CANONNAME; //返回规范名.
hints.ai_socktype = SOCK_STREAM; //只需要SOCK_STREAM套接字类型
hints.ai_protocol = IPPROTO_TCP; //只需要TCP协议的.
//
const char* nodename = "www.baidu.com";
struct evdns_getaddrinfo_request *req;
req = evdns_getaddrinfo(dnsbase , nodename , NULL , &hints , callback , NULL);
//
event_base_dispatch(base);
//
if ( req != NULL) free( req );
//
evdns_base_free(dnsbase, 0);
event_base_free(base);
}
libevent笔记-dns解析
最新推荐文章于 2022-01-12 01:01:16 发布