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
相关产品推荐
相关产品推荐

