首页 > 代码库 > 使用libevent异步解析dns

使用libevent异步解析dns

  libevent 自带 dns 解析库,支持同步、异步两种方式解析域名。因 libevent 本身是异步事件驱动型类库,我们在基于它做应用时,也多数是使用异步模型,因此这里介绍一下如何使用 libevent 异步解析域名。 libevent 官网有文章专门介绍 DNS 功能,请参考《Using DNS with Libevent》。

    我这里的例子和官网稍有不同,主要体现在对 DNS 服务器的配置上,区分了多平台。因为我发现在 Android (安卓)平台上, libevent 在获取 DNS 服务器时有问题,具体请参考另一篇博文《libevent 在 Android 上的一个改进》。

    我使用 libevent-2.1.3-alpha (请到官网下载)和 Qt 5.2 。关于 Qt 环境搭建,参考《Windows下Qt 5.2 for Android开发入门》。

    下面是所有代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct event_base * g_evbase = 0;  
  2. struct evdns_base * g_dnsbase = 0;  
  3.   
  4. static void _dns_callback(int errcode, struct evutil_addrinfo *addr, void *ptr)  
  5. {  
  6.     if (errcode)  
  7.     {  
  8.         printf("%s -> %s\n", (char*)ptr, evutil_gai_strerror(errcode));  
  9.     }  
  10.     else  
  11.     {  
  12.         struct evutil_addrinfo *ai;  
  13.         char ip[128];  
  14.         printf("dns resolved,hostname - %s, ip :\n", (char*)ptr);  
  15.         for (ai = addr; ai; ai = ai->ai_next)  
  16.         {  
  17.             const char *s = NULL;  
  18.             if (ai->ai_family == AF_INET)  
  19.             {  
  20.                 struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;  
  21.                 s = evutil_inet_ntop(AF_INET, &sin->sin_addr, ip, 128);  
  22.             }  
  23.             else if (ai->ai_family == AF_INET6)  
  24.             {  
  25.                 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;  
  26.                 s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, ip, 128);  
  27.             }  
  28.             if(s)  
  29.             {  
  30.                 printf("  %s\n", s);  
  31.             }  
  32.         }  
  33.     }  
  34.   
  35.     if(addr)evutil_freeaddrinfo(addr);  
  36. }  
  37.   
  38.   
  39. struct evdns_base * setup_evdns_base(struct event_base *base)  
  40. {  
  41.     if(g_dnsbase)  
  42.     {  
  43.         return g_dnsbase;  
  44.     }  
  45.     else  
  46.     {  
  47.         struct evdns_base * dnsbase = 0;  
  48. #if defined(_WIN32)  
  49.         dnsbase = evdns_base_new(base, 0);  
  50.         evdns_base_nameserver_ip_add(dnsbase, "8.8.8.8");  
  51. #elif defined(ANDROID)  
  52.         dnsbase = evdns_base_new(base, 0);  
  53.         {  
  54.             int ret = 0;  
  55.             int contains_default = 0;  
  56.             char buf[PROP_VALUE_MAX];  
  57.             ret = __system_property_get("net.dns1", buf);  
  58.             if(ret >= 7)  
  59.             {  
  60.                 if(!strncmp("8.8.8.8", buf, 7)) contains_default = 1;  
  61.                 evdns_base_nameserver_ip_add(dnsbase, buf);  
  62.             }  
  63.             ret = __system_property_get("net.dns2", buf);  
  64.             if(ret >= 7)  
  65.             {  
  66.                 if(!strncmp("8.8.8.8", buf, 7)) contains_default = 1;  
  67.                 evdns_base_nameserver_ip_add(dnsbase, buf);  
  68.             }  
  69.             if(!contains_default)  
  70.             {  
  71.                 evdns_base_nameserver_ip_add(dnsbase, "8.8.8.8");  
  72.             }  
  73.         }  
  74. #else  
  75.         dnsbase = evdns_base_new(base, 1);  
  76. #endif  
  77.         printf(" dns server count : %d\n", evdns_base_count_nameservers(dnsbase));  
  78.   
  79.         g_dnsbase = dnsbase;  
  80.         return dnsbase;  
  81.     }  
  82. }  
  83.   
  84.   
  85. static int lookup_host(const char * host)  
  86. {  
  87.     struct evutil_addrinfo hints;  
  88.     struct evdns_getaddrinfo_request *req;  
  89.     memset(&hints, 0, sizeof(hints));  
  90.     hints.ai_family = AF_UNSPEC;  
  91.     hints.ai_flags = EVUTIL_AI_CANONNAME;  
  92.     hints.ai_socktype = SOCK_STREAM;  
  93.     hints.ai_protocol = IPPROTO_TCP;  
  94.   
  95.     req = evdns_getaddrinfo(g_dnsbase, host, NULL ,  
  96.                 &hints, _dns_callback, (void*)host);  
  97.     if (req == NULL)  
  98.     {  
  99.         printf("    [request for %s returned immediately]\n", host);  
  100.         return -1;  
  101.     }  
  102.     return 0;  
  103. }  
  104.   
  105.   
  106.   
  107. int main(int argc, char **argv)  
  108. {  
  109. #ifdef WIN32  
  110.     WORD wVersionRequested;  
  111.     WSADATA wsaData;  
  112.   
  113.     wVersionRequested = MAKEWORD(2, 2);  
  114.   
  115.     (void) WSAStartup(wVersionRequested, &wsaData);  
  116. #endif  
  117.   
  118.     if(argc < 2)  
  119.     {  
  120.         printf("Usage: \n    dns_resolv hostname\n");  
  121.         return 0;  
  122.     }  
  123.   
  124.     g_evbase = event_base_new();  
  125.     setup_evdns_base(g_evbase);  
  126.     if(lookup_host(argv[1]) == 0)  
  127.     {  
  128.         event_base_loop(g_evbase, EVLOOP_NO_EXIT_ON_EMPTY);  
  129.     }  
  130.   
  131.     event_base_free(g_evbase);  
  132.     evdns_base_free(g_dnsbase, 1);  
  133.   
  134. #ifdef WIN32  
  135.     (void) WSACleanup();  
  136. #endif  
  137.     return 0;  
  138. }  


    代码比较简单,从命令行获取待解析域名进行解析。通过 WIN32 ,ANDROID 这样一些宏来区分不同平台。

    需要说明一点,在我的 Win7 环境下,通过 evdns_base_new(base, 1) 这种方式无法读取到域名服务器,所以我配置了 google 的 8.8.8.8 来使用。

使用libevent异步解析dns