有没有办法在 swift 或 ObjC 中为 IPv4 和 IPv6 发出单独的 DNS 请求

Is there a way to make separate DNS requests for IPv4 and IPv6 in swift or ObjC

我目前拥有的是:

void startQueryIPv4(const char *hostName){
printf("startQueryIPv4");
DNSServiceRef serviceRef;
DNSServiceGetAddrInfo(&serviceRef, kDNSServiceFlagsForceMulticast, 0, kDNSServiceProtocol_IPv4, hostName, queryIPv4Callback, NULL);
DNSServiceProcessResult(serviceRef);
DNSServiceRefDeallocate(serviceRef);
}


static void queryIPv4Callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context){
printf("queryIPv4Callback");

if (errorCode == kDNSServiceErr_NoError) {

    printf("no error");
    char *theAddress = NULL;

    switch(address->sa_family) {
        case AF_INET: {
            struct sockaddr_in *addr_in = (struct sockaddr_in *)address;
            theAddress = malloc(INET_ADDRSTRLEN);
            inet_ntop(AF_INET, &(addr_in->sin_addr), theAddress, INET_ADDRSTRLEN);
            break;
        }
        case AF_INET6: {
            struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)address;
            theAddress = malloc(INET6_ADDRSTRLEN);
            inet_ntop(AF_INET6, &(addr_in6->sin6_addr), theAddress, INET6_ADDRSTRLEN);
            break;
        }
        default:
            break;
    }
    printf("IP address: %s\n", theAddress);
    free(theAddress);

} else {
    printf("%d", errorCode);
}

}

但从未调用回调。 在控制台中,我收到此错误:TIC Read Status [9:0x0]: 1:57 ObjectiveC 不是我的力量,但我不得不搞砸它。任何帮助将不胜感激。

因为 Objective-C 是 C 的真正超集并且 Darwin 被证明与 SUS 3 兼容,所以您的 Objective-C 程序 iOS 应该能够使用 C 接口到系统的名称解析器:getaddrinfo()。您可以使用此函数的第三个参数来指定您只需要 IPv4 结果(或只需要 IPv6 结果)。

你应该知道的事情:

  • 这当然是一个同步接口;如果你想要异步,那么你需要自己安排。

  • getaddrinfo() 分配和 returns 一个地址链表,所以

    • 原则上,您可能需要检查多个

    • 您需要在完成后通过 freeaddrinfo()

    • 释放列表

您没有收到回调的原因是您没有使用调度队列。 DNSServiceGetAddrInfo API 是异步的。如果您在 Apple 设备上执行此操作,您需要更像这样的东西:

void startQueryIPv4(const char *hostName) {
  printf("startQueryIPv4");
  DNSServiceRef serviceRef;
  DNSServiceGetAddrInfo(&serviceRef, kDNSServiceFlagsForceMulticast, 0, kDNSServiceProtocol_IPv4, hostName, queryIPv4Callback, NULL);
  main_queue = dispatch_get_main_queue();
  DNSServiceSetDispatchQueue(sdref, main_queue);
  dispatch_main();
}

注意 dispatch_main() 是 libdispatch 的主要事件循环:如果你想做其他事情,你需要在调度循环中安排它,因为 dispatch_main() 不会return.

在您的原始代码中,您调用了 DNSServiceRefDeallocate(),但是在您想要停止查询之前您不能这样做。如果您在开始查询后立即调用它,它将取消查询。所以例如你可以从回调中调用它。

但是,更好的流程是执行长期查询 (kDNSServiceFlagsLongLivedQuery),以便在信息发生变化时获得更新。当然,您随后需要更改要连接的主机,因此只有在您的应用程序将长时间连接时才这样做。

此外,您可能会得到不止一个答案。如果这样做,则可能是某些答案有助于建立联系,而其他答案则不然。因此,您可能希望积累答案并尝试每一个,而不是如果您得到的第一个答案不起作用就放弃。如果立即有更多数据,回调将包括 kDNSServiceFlagsMoreComing 标志。每次回调被调用时,它都会得到一个答案(或以某种方式表明查询失败)。

当然,这是一个相当低级的API。如果你想让你的生活更轻松一些,你应该使用网络框架。 Network Framework 为您做了“快乐的眼球”部分——尝试每个响应直到它获得连接,然后 return 向您发送它获得的连接,取消其他连接。

但是你没有问这个,这里就不多说了。