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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:08:48