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

Swift中使用NetService无法获取网络服务地址与端口的问题

解决Swift中NetService无法获取IP地址和端口的问题

当NetServiceBrowser通过didFind service回调返回服务时,这个服务尚未被解析,此时hostName、port、addresses都是未初始化的状态,所以会出现port=-1、addresses为空数组的情况。必须主动触发服务解析,才能拿到完整的网络信息。

具体修改方案

  • 新增数组存储找到的NetService实例,避免对象被提前释放
  • 在找到服务后,调用resolve(withTimeout:)方法触发解析
  • 实现NetServiceDelegate的解析完成回调方法,在回调中获取完整的服务信息
  • 可选:处理解析失败的情况

修改后的完整代码

import Foundation
import Network

class SocketIoManager: NSObject, NetServiceBrowserDelegate, NetServiceDelegate {
   
    private var browser: NetServiceBrowser!
    // 存储找到的服务,避免被提前释放
    private var discoveredServices = [NetService]()

    private override init() {
        super.init()
        browser = NetServiceBrowser()
        browser.delegate = self
        browser.searchForServices(ofType: "_itworks._tcp", inDomain: "")
    }
    
    // MARK: - NetServiceBrowserDelegate
    func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
        // 保存服务实例
        discoveredServices.append(service)
        // 设置代理并触发解析,超时时间可根据需求调整
        service.delegate = self
        service.resolve(withTimeout: 5.0)
    }
    
    func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool) {
        // 移除已消失的服务,可选
        if let index = discoveredServices.firstIndex(of: service) {
            discoveredServices.remove(at: index)
        }
    }
    
    // MARK: - NetServiceDelegate
    func netServiceDidResolveAddress(_ service: NetService) {
        // 解析完成,此时可以获取完整的服务信息
        let serviceInfo = serializeService(toDictionary: service)
        print("解析完成的服务信息:\(serviceInfo)")
        
        // 可选:解析完成后停止监听该服务,清理内存
        service.stop()
        if let index = discoveredServices.firstIndex(of: service) {
            discoveredServices.remove(at: index)
        }
    }
    
    func netService(_ service: NetService, didNotResolve errorDict: [String : NSNumber]) {
        // 处理解析失败的情况
        print("服务解析失败:\(errorDict)")
        if let index = discoveredServices.firstIndex(of: service) {
            discoveredServices.remove(at: index)
        }
    }
    
    // MARK: - 工具方法
    private func serializeService(toDictionary service: NetService) -> [String: Any] {
        var serviceInfo: [String: Any] = [:]
        serviceInfo["name"] = service.name
        serviceInfo["host"] = service.hostName
        serviceInfo["port"] = service.port
        serviceInfo["addresses"] = addressesFromService(service)
        return serviceInfo
    }
    
    func addressesFromService(_ service: NetService) -> [String] {
        var addresses = [String]()
        guard let serviceAddresses = service.addresses else { return addresses }

        var addressBuffer = [CChar](repeating: 0, count: Int(INET6_ADDRSTRLEN))

        for data in serviceAddresses {
            memset(&addressBuffer, 0, Int(INET6_ADDRSTRLEN))

            typealias IPSocketAddress = (
                sa: sockaddr,
                ipv4: sockaddr_in,
                ipv6: sockaddr_in6
            )

            let socketAddress = data.withUnsafeBytes { pointer -> IPSocketAddress in
                return pointer.baseAddress!.assumingMemoryBound(to: IPSocketAddress.self).pointee
            }

            if socketAddress.sa.sa_family == sa_family_t(AF_INET) || socketAddress.sa.sa_family == sa_family_t(AF_INET6) {
                let addressStr = socketAddress.sa.sa_family == sa_family_t(AF_INET)
                    ? inet_ntop(AF_INET, &socketAddress.ipv4.sin_addr, &addressBuffer, socklen_t(INET6_ADDRSTRLEN))
                    : inet_ntop(AF_INET6, &socketAddress.ipv6.sin6_addr, &addressBuffer, socklen_t(INET6_ADDRSTRLEN))

                if let addressStr = addressStr, let address = String(utf8String: addressStr) {
                    addresses.append(address)
                }
            }
        }

        return addresses
    }
}

关键说明

  • 必须持有NetService实例:如果不将服务存入discoveredServices数组,服务对象可能会被ARC提前释放,导致解析回调无法触发。
  • 解析超时设置:resolve(withTimeout:)的超时时间可以根据网络环境调整,单位是秒。
  • 解析完成后的清理:解析完成或失败后,建议从数组中移除对应的服务,并调用service.stop()停止监听,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Pastor J.G Seigle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:25:18