You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS端通过代码从局域网IP获取主机名失败,三种方法均无效

iOS局域网IP反向解析主机名修复方案

原方法问题分析与修复

方法2:POSIX getaddrinfo + getnameinfo 修复

原代码存在两个关键问题:

  • getaddrinfo调用时未传入配置好的hints结构体,导致参数配置无效
  • getnameinfo未设置NI_NAMEREQD标志,解析失败时直接返回原IP而非空结果

修复后的代码:

+ (NSString *)getHostFromIPAddress:(NSString*)ipAddress {
    struct addrinfo *result = NULL;
    struct addrinfo hints;

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_NUMERICHOST; // 指定输入为IP地址,避免正向DNS解析
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    int error = getaddrinfo([ipAddress cStringUsingEncoding:NSASCIIStringEncoding], 
                           NULL, &hints, &result);
    if (error != 0) {
        NSLog(@"getaddrinfo error: %s", gai_strerror(error));
        return @"";
    }
    
    NSString *hostname = @"";
    for (struct addrinfo *r = result; r; r = r->ai_next) {
        char hostBuffer[NI_MAXHOST] = {0};
        // 设置NI_NAMEREQD,强制要求返回主机名,失败则返回错误
        error = getnameinfo(r->ai_addr, r->ai_addrlen, 
                           hostBuffer, sizeof(hostBuffer), 
                           NULL, 0, NI_NAMEREQD);
        if (error == 0) {
            hostname = [NSString stringWithUTF8String:hostBuffer];
            break;
        } else {
            NSLog(@"getnameinfo error: %s", gai_strerror(error));
        }
    }

    freeaddrinfo(result);
    return hostname;
}

方法3:CFHostRef 异步解析修复

原代码使用同步解析方式,在iOS 14+及模拟器环境下易因系统权限或限制失败,改为异步解析并完善错误处理:

+ (void)getHostFromIPAddressAsync:(NSString *)ipAddress completion:(void(^)(NSString *hostname))completion {
    struct addrinfo *result = NULL;
    struct addrinfo hints;
    
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;
    
    int errorStatus = getaddrinfo([ipAddress cStringUsingEncoding:NSASCIIStringEncoding], 
                                 NULL, &hints, &result);
    if (errorStatus != 0) {
        NSLog(@"getaddrinfo error: %s", gai_strerror(errorStatus));
        completion(nil);
        return;
    }
    
    CFDataRef addressRef = CFDataCreate(NULL, (UInt8 *)result->ai_addr, result->ai_addrlen);
    freeaddrinfo(result);
    
    if (!addressRef) {
        completion(nil);
        return;
    }
    
    CFHostRef hostRef = CFHostCreateWithAddress(kCFAllocatorDefault, addressRef);
    CFRelease(addressRef);
    
    if (!hostRef) {
        completion(nil);
        return;
    }
    
    // 设置异步解析回调
    CFHostClientContext context = {0, (__bridge void *)completion, NULL, NULL, NULL};
    CFHostSetClient(hostRef, ^(CFHostRef host, CFHostInfoType type, const CFStreamError *error, void *info) {
        if (type == kCFHostNames) {
            NSString *hostname = nil;
            CFArrayRef names = CFHostGetNames(host, NULL);
            if (names && CFArrayGetCount(names) > 0) {
                hostname = (__bridge NSString *)CFArrayGetValueAtIndex(names, 0);
            }
            ((void(^)(NSString *))info)(hostname);
            CFRelease(host);
        }
    }, &context);
    
    CFHostScheduleWithRunLoop(hostRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    BOOL started = CFHostStartInfoResolution(hostRef, kCFHostNames, NULL);
    if (!started) {
        CFRelease(hostRef);
        completion(nil);
    }
}

调用示例:

[YourClass getHostFromIPAddressAsync:@"192.168.1.100" completion:^(NSString *hostname) {
    if (hostname) {
        NSLog(@"解析到主机名:%@", hostname);
    } else {
        NSLog(@"解析失败");
    }
}];

关键注意事项

  • iOS 14+ 局域网权限:必须在Info.plist中添加NSLocalNetworkUsageDescription(填写局域网访问用途描述),否则无法访问局域网设备服务或进行解析。
  • 模拟器限制:部分模拟器环境下局域网解析存在兼容性问题,建议使用真实iPhone设备测试。
  • 设备兼容性:目标设备需开启Bonjour服务或支持反向DNS解析,否则无法获取主机名。

替代方案:Bonjour局域网扫描

若反向DNS解析失败,可通过Bonjour协议直接扫描局域网设备,获取主机名与IP映射:

#import <NetServices/NetServices.h>

@interface YourClass () <NSNetServiceBrowserDelegate, NSNetServiceDelegate>
@property (nonatomic, strong) NSMutableArray *devices;
@end

@implementation YourClass

+ (void)scanLANDevicesWithCompletion:(void(^)(NSArray<NSDictionary *> *devices))completion {
    YourClass *instance = [[YourClass alloc] init];
    instance.devices = [NSMutableArray array];
    
    NSNetServiceBrowser *browser = [[NSNetServiceBrowser alloc] init];
    browser.delegate = instance;
    
    // 扫描HTTP服务,可根据需求修改服务类型
    [browser searchForServicesOfType:@"_http._tcp." inDomain:@"local."];
    
    // 5秒后停止扫描并返回结果
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        [browser stop];
        completion([instance.devices copy]);
    });
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing {
    [service resolveWithTimeout:5.0];
    service.delegate = self;
}

- (void)netServiceDidResolveAddress:(NSNetService *)service {
    // 提取设备IP地址
    NSMutableArray *ipAddresses = [NSMutableArray array];
    for (NSData *addrData in service.addresses) {
        char addrBuffer[INET6_ADDRSTRLEN];
        struct sockaddr_in6 *addr = (struct sockaddr_in6 *)[addrData bytes];
        if (inet_ntop(AF_INET6, &addr->sin6_addr, addrBuffer, sizeof(addrBuffer))) {
            [ipAddresses addObject:[NSString stringWithUTF8String:addrBuffer]];
        }
    }
    
    NSDictionary *deviceInfo = @{@"hostname": service.name,
                                @"ipAddresses": ipAddresses};
    [self.devices addObject:deviceInfo];
}

@end

内容的提问来源于stack exchange,提问作者Afinainflowers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:48:14