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

WebRTC fetchUserIpAddress本地正常生产环境失效问题排查与解决

问题原因分析
  • ICE服务器配置缺失:本地环境下WebRTC无需STUN服务器就能获取内网IP,但生产环境中,没有STUN服务器的话,浏览器无法生成包含公网IP的ICE候选,甚至可能不触发有效的候选事件。
  • ICE候选事件处理逻辑错误:当前代码在event.candidate为null时直接reject,但null候选是所有候选收集完成的标志,此时可能已经有有效IP候选被忽略;同时只处理第一个候选,若第一个候选不包含可匹配的IP就直接失败。
  • 正则表达式覆盖不全:现有正则只能匹配完整的IPv4和标准IPv6,无法处理压缩形式的IPv6(如2001:db8::1)或候选字符串中其他位置的IP。
  • 非安全上下文限制:如果生产环境使用HTTP而非HTTPS,现代浏览器会限制WebRTC API的使用,导致无法生成ICE候选。
修改后的代码
export const fetchUserIpAddress = (): Promise<string> => {
    return new Promise<string>((resolve, reject) => {
        // 设置超时,防止无限等待
        const timeout = setTimeout(() => {
            reject("获取IP超时");
            rtc.close();
        }, 5000);

        const rtc = new RTCPeerConnection({
            iceServers: [
                { urls: "stun:stun.l.google.com:19302" } // 添加公共STUN服务器
            ]
        });

        let foundIp: string | null = null;

        rtc.createDataChannel("");
        rtc.createOffer()
            .then((offer) => rtc.setLocalDescription(offer))
            .catch((error) => {
                clearTimeout(timeout);
                reject(error);
                rtc.close();
            });

        rtc.onicecandidate = (event) => {
            if (event.candidate) {
                const candidate = event.candidate.candidate;
                // 覆盖IPv4、压缩/完整IPv6的正则
                const ipRegex = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:(?:(:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/;
                const ipMatch = candidate.match(ipRegex);
                
                if (ipMatch && !foundIp) {
                    foundIp = ipMatch[0];
                    // 优先返回公网IP,过滤常见内网段
                    if (!foundIp.startsWith("192.168.") && !foundIp.startsWith("10.") && !foundIp.startsWith("172.") && !foundIp.startsWith("fe80:")) {
                        clearTimeout(timeout);
                        resolve(foundIp);
                        rtc.close();
                    }
                }
            } else {
                // 所有候选收集完成后统一处理
                clearTimeout(timeout);
                foundIp ? resolve(foundIp) : reject("未找到可用IP地址");
                rtc.close();
            }
        };
    });
};
修改说明
  • 添加STUN服务器:借助公共STUN服务器获取公网IP,解决生产环境无法生成有效候选的问题。
  • 优化候选处理逻辑:收集所有ICE候选直到事件结束,优先筛选公网IP,避免遗漏有效地址。
  • 增强正则表达式:支持压缩格式IPv6等更多IP场景,提升匹配成功率。
  • 添加超时机制:避免因网络问题导致Promise长期处于pending状态。
  • 安全上下文要求:确保生产环境使用HTTPS,现代浏览器仅允许在安全上下文下调用WebRTC API。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 14:15:56