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

C#检测公网IP变更异常:VPN开启后无法识别IP变化问题

公网IP变更检测异常问题分析

问题背景

使用以下C#代码跟踪桌面应用的公网IP变更,可检测公网IP本身变化或VPN切换IP的情况,代码在应用启动及需要检查时执行:

public class PublicIP
{
    IPAddress last_ip=null;
    DateTime timestamp_lastipchange;

    public void UpdateIP()
    {
            List<string> hosts = new List<string>()
            {
                "https://api.ipify.org",
                "https://ipinfo.io/ip",
                "https://checkip.amazonaws.com",
                "https://wtfismyip.com/text",
                "http://icanhazip.com"
            };
            using(WebClient webclient = new WebClient())
            {
                foreach(string host in hosts)
                {
                    //Download each string from hosts until an IP could be fetched
                    try{
                        var newip = IPAddress.Parse(webclient.DownloadString(service)); //Downloading the string
                        if(!newip.IsEqual(last_ip) && last_ip!=null) timestamp_lastipchange = DateTime.Now; //Check if the ip changed, if the last known ip does not exists skipp this step
                        last_ip = newip; //Save last known ip
                        return;
                        } 
                    catch { }
                }
            }
    }
}

测试异常场景

多数场景运行正常,但单元测试发现以下场景无法检测IP变化:

  • 切换网络导致IP变化:可成功检测
  • 运营商变更IP:可成功检测
  • 应用启动时VPN已开启,随后关闭:可成功检测
  • 应用启动时VPN关闭,运行中开启VPN:无法检测到变化,WebClient.DownloadString()仍返回VPN开启前的IP

疑问

该场景下无法检测IP变化的原因是什么?是否需要手动选择VPN网络接口,或是存在客户端/服务端缓存问题?


原因分析

1. 客户端连接池与路由缓存问题

当应用启动时VPN未开启,系统会为IP检测服务解析DNS并建立TCP连接。开启VPN后,系统路由表更新,但WebClient基于的HttpWebRequest默认启用连接池复用机制,已建立的旧连接不会自动断开,后续请求仍通过旧连接(非VPN路由)发送,因此返回的还是VPN开启前的IP。

另外,系统DNS缓存也可能导致服务域名解析结果未更新,但核心问题还是连接复用。

2. 服务端缓存排除

IP检测服务都是实时返回当前请求的源IP,不会缓存单个客户端的IP信息,所以服务端缓存不是问题。

3. 无需手动选择VPN网络接口

手动指定网络接口操作复杂且不通用,不同VPN的接口标识差异大,不是可行的解决方案。


解决方案

针对连接复用问题,可通过以下方式修正:

方案1:禁用WebClient连接复用

在请求头中添加Connection: Close,强制每次请求后关闭连接,避免复用旧连接:

public void UpdateIP()
{
    List<string> hosts = new List<string>()
    {
        "https://api.ipify.org",
        "https://ipinfo.io/ip",
        "https://checkip.amazonaws.com",
        "https://wtfismyip.com/text",
        "http://icanhazip.com"
    };
    using(WebClient webclient = new WebClient())
    {
        // 添加Close头禁用连接复用
        webclient.Headers.Add(HttpRequestHeader.Connection, "Close");
        foreach(string host in hosts)
        {
            try{
                // 修正原代码变量错误:service改为host
                var newip = IPAddress.Parse(webclient.DownloadString(host));
                if(last_ip != null && !newip.Equals(last_ip)) 
                    timestamp_lastipchange = DateTime.Now;
                last_ip = newip;
                return;
            } 
            catch { }
        }
    }
}

方案2:替换为HttpClient(推荐)

HttpClient支持更灵活的连接池配置,可设置连接生命周期限制,避免长期复用旧连接:

// 建议全局单例HttpClient,避免频繁创建
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler
{
    // 设置连接存活时间,到期自动回收
    PooledConnectionLifetime = TimeSpan.FromMinutes(1)
});

public async Task UpdateIPAsync()
{
    List<string> hosts = new List<string>()
    {
        "https://api.ipify.org",
        "https://ipinfo.io/ip",
        "https://checkip.amazonaws.com",
        "https://wtfismyip.com/text",
        "http://icanhazip.com"
    };

    foreach(string host in hosts)
    {
        try{
            var ipStr = await _httpClient.GetStringAsync(host);
            var newip = IPAddress.Parse(ipStr.Trim());
            if(last_ip != null && !newip.Equals(last_ip)) 
                timestamp_lastipchange = DateTime.Now;
            last_ip = newip;
            return;
        } 
        catch { }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:46:32