最可靠方式是调用第三方IP归属地API;推荐ip-api.com或ipapi.co(免费限1000次/天),解析JSON获取city、region_name等字段;本地用qqwry.dat需按二进制结构解析,注意重定向标志;GeoLite2需官方库且免费版无ISP数据;精度受限于IP分配滞后性与代理覆盖。

直接用第三方 IP 归属地 API 是最可靠、最省事的方式;自己维护 IP 库虽然可控,但更新成本高、格式易出错,新手容易卡在解析 qqwry.dat 或处理 CIDR 上。
用免费 HTTP API(推荐新手)
国内可用的稳定免费接口极少,ip.taobao.com 已停用,目前较稳的是 ip-api.com(有调用频率限制)和国内镜像如 ip.51240.com。注意:后者返回是 HTML 片段,需解析;前者返回 JSON,更干净。
- 调用
https://ipapi.co/{ip}/json/(支持 IPv4/IPv6,免费限 1000 次/天,无需 key) - 用
HttpClient发 GET 请求,检查响应状态码是否为200,再反序列化 JSON - 关键字段:
city(城市)、region_name(省份)、org(运营商或 ASN 描述),不是所有 IP 都能精确到城市,尤其内网或 CDN 节点 - 别硬编码超时时间——设
HttpClient.Timeout = TimeSpan.FromSeconds(5),避免线程卡死
加载本地纯真 IP 库(qqwry.dat)
qqwry.dat 是二进制格式,不是文本,不能用 StreamReader 直接读;必须按其固定结构解析起始索引区、记录区和重定向标志(0x01 / 0x02)。网上多数 C# 实现漏处理“国家/地区”字段嵌套重定向,导致运营商显示为空。
- 下载最新版
qqwry.dat(如从https://github.com/17mon/china_ip_list获取),确保文件未被 Windows 自动解压损坏 - 核心逻辑在
FindIP方法:先查索引区定位记录偏移,再读取原始数据,遇到0x01要跳转到新地址再读,遇到0x02则从偏移 0x100 开始读国家字段 - C# 中用
BinaryReader配合BaseStream.Seek()控制读取位置,别用File.ReadAllBytes全部加载——大库(>5MB)会吃内存 - IPv6 不支持,此库仅限 IPv4;查询前务必用
IPAddress.TryParse(ipStr, out var addr) && addr.AddressFamily == AddressFamily.InterNetwork校验
用 GeoLite2(MaxMind 官方方案)
这是目前最规范的方案,但免费版 GeoLite2-City.mmdb 需要注册账号下载,且 C# 必须用官方库 MaxMind.Db + GeoIP2,不能手写解析器。
- NuGet 安装
GeoIP2(它已依赖MaxMind.Db),不要只装后者 - 初始化
var reader = new DatabaseReader("GeoLite2-City.mmdb"),注意路径必须存在且进程有读权限 - 查城市:
reader.City(ipAddress).City.Name;查运营商需用reader.Isp(ipAddress).Isp——但免费库不含 ISP 数据,必须升级到付费版或换用商业 API - 并发场景下,
DatabaseReader是线程安全的,可单例复用;别每次查询都 new 一个
真正难的不是调通某个接口,而是意识到:公网 IP 查归属地本质是查“谁分配了这个段”,而分配信息本身滞后、模糊、常被代理覆盖。比如 CDN 回源 IP 显示的是阿里云杭州节点,实际用户可能在乌鲁木齐。别指望 100% 准确,重点看业务容忍度——登录风控够用就行,司法取证则必须用运营商实名数据。


















