Swift(iOS/macOS)无需第三方URL获取公网IP:PortMapper返回nil求助
在iOS/macOS用Swift获取公网IP(无需第三方URL)
嘿,我来帮你搞定这个问题!你用PortMapper.findPublicAddress()返回nil的情况其实挺常见的,主要是这个API依赖UPnP或NAT-PMP协议,很多网络环境(比如公司内网、公共WiFi)会禁用这些协议,或者你的路由器没开相关功能。下面给你两种可行的方案,还有详细的代码示例:
一、正确使用PortMapper的姿势
首先,PortMapper不是直接调用类方法就能拿到结果的,它需要通过代理回调异步获取地址。试试下面的完整实现:
import Foundation import PortMapper class PublicIPFetcher { private var portMapper: PortMapper? func fetchPublicIP(completion: @escaping (String?) -> Void) { portMapper = PortMapper() portMapper?.delegate = self // 启动PortMapper,开始扫描网络中的NAT设备 portMapper?.start() } } extension PublicIPFetcher: PortMapperDelegate { // 成功获取到公网地址的回调 func portMapper(_ portMapper: PortMapper, didFindPublicAddress address: Data?) { portMapper.stop() guard let addressData = address, let ipString = String(data: addressData, encoding: .utf8) else { completion(nil) return } completion(ipString.trimmingCharacters(in: .whitespacesAndNewlines)) } // 出错时的回调 func portMapper(_ portMapper: PortMapper, didFailWithError error: Error) { portMapper.stop() completion(nil) print("PortMapper出错: \(error.localizedDescription)") } } // 调用示例 let ipFetcher = PublicIPFetcher() ipFetcher.fetchPublicIP { publicIP in if let ip = publicIP { print("拿到公网IP啦: \(ip)") } else { print("没拿到公网IP,可能是网络不支持UPnP/NAT-PMP") } }
注意:记得在Xcode的项目设置里,把PortMapper框架添加到Link Binary With Libraries中,不然会编译报错。如果用了这段代码还是返回nil,那基本就是你的网络环境不支持UPnP/NAT-PMP了,换下面的方案试试。
二、用Network框架获取公网IP
这个方法不需要依赖UPnP协议,而是通过建立一个UDP连接到公共服务器(比如谷歌的DNS服务器8.8.8.8),从连接的属性里拿到NAT映射后的公网IP。而且不需要发送任何数据,只是建立连接,流量开销极小:
import Network func getPublicIP(completion: @escaping (String?) -> Void) { // 连接到谷歌公共DNS的UDP端口 let udpConnection = NWConnection(host: "8.8.8.8", port: 53, using: .udp) udpConnection.stateUpdateHandler = { connectionState in switch connectionState { case .ready: // 连接就绪后,从currentPath里拿到本地的公网端点 if let localEndpoint = udpConnection.currentPath?.localEndpoint as? NWHostEndpoint { completion(localEndpoint.host) } else { completion(nil) } udpConnection.cancel() case .failed(let error): print("Network框架出错: \(error.localizedDescription)") completion(nil) udpConnection.cancel() default: // 其他状态(正在连接、等待中)不处理 break } } // 启动连接 udpConnection.start(queue: DispatchQueue.global()) } // 调用示例 getPublicIP { publicIP in if let ip = publicIP { print("公网IP是: \(ip)") } else { print("获取公网IP失败") } }
为啥PortMapper会返回nil?
给你列几个常见原因:
- 你的网络环境禁用了UPnP/NAT-PMP协议(比如公司防火墙、公共WiFi)
- 家用路由器没开启UPnP或NAT-PMP功能(去路由器设置里看看)
- 设备处于双重NAT环境(比如路由器后面还有一层路由器,这种情况UPnP通常失效)
- 之前的代码调用方式错误(直接同步调用类方法,没等待异步回调)
内容的提问来源于stack exchange,提问作者Siva Prasad
相关产品推荐
相关产品推荐

