
后端网络【免费下载链接】ip2regionIp2region is an offline IP-to-Region localization library and IP data management framework with both IPv4 and IPv6 supports, 10-microsecond level query efficiency, xdb search client for many programming languages项目地址https://gitcode.com/GitHub_Trending/ip/ip2region点击查看免费下载Ip2region 是一个离线 IP 到地域的定位库与 IP 数据管理框架支持 IPv4 与 IPv6查询效率可达微秒级。本指南以仓库中的 C 语言查询客户端文档 为主体完整讲解其查询 API、xdb 文件适用性验证、三种缓存查询模式file / vectorIndex / content、region 信息的内存管理以及基于xdb_searcher可执行程序的交互式查询与 bench 基准测试方法并结合作业源码xdb_api.h、xdb_searcher.c、main.c揭示底层二分查找与 IO 计数原理。读完本文你将能够独立完成 xdb 查询客户端的编译、集成与性能验证。一、查询 API 速览C 查询客户端对外暴露的查询接口只有两个其原型定义于 xdb_api.h// 通过字符串 IP 进行查询 int xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *); // 通过 xdb_parse_ip 返回的二进制 IP 进行查询 int xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);两者的语义规则查询失败时返回非0的错误代码查询成功时xdb_region_buffer_t中可获取字符串形式的region信息若输入的 IP 在 xdb 中找不到对应记录xdb_region_buffer_t会得到空字符串。从源码实现看xdb_search_by_string是xdb_search的便捷封装它先调用xdb_parse_ip将字符串形式的 IP 解析为字节数组再调用xdb_search执行查询见 xdb_searcher.c。因此如果你在业务中已经持有二进制 IP例如从数据流中直接解析出的字节序列可以跳过字符串解析直接调用xdb_search以减少一次解析开销。二、IPv4 与 IPv6 双栈支持该 C 查询客户端同时支持对 IPv4 和 IPv6 的查询版本通过xdb_version_t *指针指定#include xdb_api.h; // 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件IP版本指定为 IPv4 const char *db_path ../../data/ip2region_v4.xdb; // 或者你的 ipv4 xdb 的路径 xdb_version_t *version XDB_IPv4; // 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件IP版本指定为 IPv6 const char *db_path ../../data/ip2region_v6.xdb; // 或者你的 ipv6 xdb 路径 xdb_version_t *version XDB_IPv6; // db_path 指定的 xdb 的 IP 版本必须和 version 指定的一致不然查询执行的时候会报错 // 备注以下演示直接使用 db_path 和 version 变量XDB_IPv4与XDB_IPv6宏在 xdb_api.h 中定义分别指向xdb_version_v4()与xdb_version_v6()返回的版本条目。从 xdb_util.c 的版本表可以看到两个版本的关键差异版本id字节数分段索引大小说明IPv444144424兼容 xdb 2.0 小端编码使用_ipv4_sub_compare比较IPv661638161624使用xdb_ip_sub_compare按序比较需要注意db_path 指定的 xdb 文件的 IP 版本必须与 version 一致否则查询执行时会报错xdb_search内部会做ip_len ! xdb-version-bytes的版本校验见 xdb_searcher.c。仓库的 data 目录 提供了开箱即用的二进制文件ip2region_v4.xdbIPv4、ip2region_v6.xdbIPv6以及对应的原始数据ipv4_source.txt、ipv6_source.txt。三、XDB 文件适用性验证推荐由于 xdb 后续新特性可能导致当前 Searcher 版本与你使用的 xdb 文件不兼容官方文档建议主动验证 xdb 文件的适用性以避免运行过程中的不可预测错误。验证不需要每次都执行——建议在服务启动时验证一次或手动调用命令确认版本匹配不要在每次创建 Searcher 时都运行验证否则会影响查询响应速度尤其是高并发场景。#include xdb_api.h; int errcode xdb_verify(db_path); if ($err ! 0) { // 适用性验证失败 // 当前查询客户端实现不适用于 db_path 指定的 xdb 文件的查询. // 应该停止启动服务使用合适的 xdb 文件或者升级到适合 db_path 的 Searcher 实现。 printf(failed to verify xdb file %s, errcode: %d\n, db_path, errcode); return; } // 验证通过当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作验证的底层逻辑在 xdb_util.c 的xdb_verify_from_header中它先根据 xdb 头部声明的结构版本xdb_structure_20对应 2.0 结构、xdb_structure_30对应 3.0 结构确定运行时指针字节数然后确认 xdb 文件大小保证最大文件指针不会溢出maxFilePtr (1LL (runtime_ptr_bytes * 8)) - 1。也就是说验证的核心是「文件结构版本可识别」且「文件大小不超过指针所能表达的范围」两点。四、模式一完全基于文件的查询这是最基本的查询方式Searcher 直接以只读方式打开 xdb 文件查询时按需读取文件中的索引与数据块。代码如下完整示例直接可编译运行#include stdio.h #include xdb_api.h int main(int argc, char *argv[]) { xdb_searcher_t searcher; char region_buffer[512] {\0}; xdb_region_buffer_t region; // 使用栈空间的 region_buffer 初始化 region_buffer_t int err xdb_region_buffer_init(region, region_buffer, sizeof(region_buffer)); if (err ! 0) { printf(failed to init the region buffer with errcode%d\n, err); return 1; } // 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用 err xdb_init_winsock(); if (err ! 0) { printf(failed to init the winsock with errno%d\n, err); return 1; } // 1、从 db_path 初始化 xdb 查询对象. // Note: 使用顶部描述的 db_path 和 version 来创建 searcher err xdb_new_with_file_only(version, searcher, db_path); if (err ! 0) { printf(failed to create xdb searcher from %s with errno%d\n, db_path, err); return 1; } // 2、调用 search API 查询IPv4 和 IPv6 都支持. const char *ip_string 1.2.3.4; // ip_string 240e:3b7:3272:d8d0:db09:c067:8d59:539e; // IPv6 long cost_time 0, s_time xdb_now(); err xdb_search_by_string(searcher, ip_string, region); cost_time (int) (xdb_now() - s_time); if (err ! 0) { printf(failed search(%s) with errno%d\n, ip_string, err); } else { printf({region: %s, took: %d μs}, region.value, cost_time); } // 清理 region 信息的内存资源每次 search 之后都得调用 xdb_region_buffer_free(region); // 备注并发使用每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 3、关闭 xdb 查询器 xdb_close(searcher); xdb_clean_winsock(); // windows 下调用 return 0; }要点说明xdb_new_with_file_only内部通过xdb_new_base以fopen(db_path, rb)打开文件并置v_index、content缓存为空见 xdb_searcher.c由于没有缓存每次查询都需要磁盘 IO源码中的read()函数在 content 缓存为空时会执行fseekfread并递增io_count见 xdb_searcher.c所以该模式下io_count会比较大交互式查询输出中可以看到并发注意文件模式下每个线程需要单独定义并初始化自己的 searcher 查询对象因为每个 searcher 持有独立的文件句柄。五、模式二缓存 VectorIndex 索引我们可以提前从 xdb 文件中加载出 VectorIndex向量索引数据并全局缓存每次创建 Searcher 对象时复用该缓存可减少一次固定的 IO 操作从而加速查询、降低 IO 压力#include stdio.h #include xdb_api.h int main(int argc, char *argv[]) { xdb_vector_index_t *v_index; xdb_searcher_t searcher; xdb_region_buffer_t region; // 使用 NULL 初始化 region_buffer让其自动管理内存的分配 int err xdb_region_buffer_init(region, NULL, 0); if (err ! 0) { printf(failed to init the region buffer with errcode%d\n, err); return 0; } // 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用 err xdb_init_winsock(); if (err ! 0) { printf(failed to init the winsock with errno%d\n, err); return 1; } // 1、从顶部描述的 db_path 加载 VectorIndex 索引。 // 得到 v_index 做成全局缓存便于后续反复使用。 // 注意v_index 不需要每次都加载建议在服务启动的时候加载一次然后做成全局资源。 v_index xdb_load_vector_index_from_file(db_path); if (v_index NULL) { printf(failed to load vector index from %s\n, db_path); return 1; } // 2、使用全局的 VectorIndex 变量创建带 VectorIndex 缓存的 xdb 查询对象. // Note: 使用顶部描述的 db_path 和 version 来创建 searcher err xdb_new_with_vector_index(version, searcher, db_path, v_index); if (err ! 0) { printf(failed to create vector index cached searcher with errcode%d\n, err); return 2; } // 3、调用 search API 查询IPv4 和 IPv6 都支持 const char *ip_string 1.2.3.4; // ip_string 240e:3b7:3272:d8d0:db09:c067:8d59:539e; // IPv6 long cost_time 0, s_time xdb_now(); err xdb_search_by_string(searcher, ip_string, region); cost_time (int) (xdb_now() - s_time); if (err ! 0) { printf(failed search(%s) with errno%d\n, ip_string, err); } else { printf({region: %s, took: %d μs}, region.value, cost_time); } // 清理 region 信息的内存资源每次 search 之后都得调用 xdb_region_buffer_free(region); // 备注并发使用每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 4、关闭 xdb 查询器如果是要关闭服务也需要释放 v_index 的内存。 xdb_close(searcher); xdb_close_vector_index(v_index); xdb_clean_winsock(); return 0; }关于 VectorIndex 的底层细节其大小由常量决定xdb_vector_index_length 524288即 256 行 × 256 列 × 8 字节见 xdb_api.hxdb_load_vector_index会从文件头部 256 字节xdb_header_info_length之后的位置一次性读入这段数据见 xdb_util.c查询时源码用 IP 前两个字节il0、il1计算向量索引偏移idx il0 * 256 * 8 il1 * 8直接从缓存的v_index-buffer中取出该段的起止指针从而省去一次文件读取见 xdb_searcher.c该模式下每个 searcher 仍持有文件句柄用于后续二分查找分段索引时的按需读取因此每个线程仍建议独立创建 searcher而v_index是只读共享资源可全局缓存、供多个 searcher 复用。六、模式三缓存整个 xdb 文件全内存查询我们也可以预先将整个 xdb 文件加载到内存然后基于这份数据创建查询对象实现完全基于内存的查询类似以往的 memory search 模式#include stdio.h #include xdb_api.h int main(int argc, char *argv[]) { xdb_content_t *c_buffer; xdb_searcher_t searcher; xdb_region_buffer_t region; // 使用 NULL 初始化 region_buffer让其自动管理内存的分配 int err xdb_region_buffer_init(region, NULL, 0); if (err ! 0) { printf(failed to init the region buffer with errcode%d\n, err); return 0; } // 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用 err xdb_init_winsock(); if (err ! 0) { printf(failed to init the winsock with errno%d\n, err); return 1; } // 1、从 顶部描述的 db_path 加载整个 xdb 的数据。 c_buffer xdb_load_content_from_file(db_path); if (v_index NULL) { printf(failed to load xdb content from %s\n, db_path); return 1; } // 2、使用全局的 c_buffer 变量创建一个完全基于内存的 xdb 查询对象. // Note: 使用顶部描述的 version 来创建 searcher. err xdb_new_with_buffer(version, searcher, c_buffer); if (err ! 0) { printf(failed to create content cached searcher with errcode%d\n, err); return 2; } // 3、调用 search API 查询IPv4 和 IPv6 都支持 const char *ip_string 1.2.3.4; // ip_string 240e:3b7:3272:d8d0:db09:c067:8d59:539e; // IPv6 long cost_time 0, s_time xdb_now(); err xdb_search_by_string(searcher, ip_string, region); cost_time (int) (xdb_now() - s_time); if (err ! 0) { printf(failed search(%s) with errno%d\n, ip_string, err); } else { printf({region: %s, took: %d μs}, region.value, cost_time); } // 清理 region 信息的内存资源每次 search 之后都得调用 xdb_region_buffer_free(region); // 备注并发使用使用这种方式创建的 xdb 查询对象可以安全用于并发。 // 建议在服务启动的时候创建好然后一直安全并发使用直到服务关闭。 // 4、关闭 xdb 查询器关闭服务的时候需要释放 c_buffer 的内存。 xdb_close(searcher); xdb_close_content(c_buffer); xdb_clean_winsock(); return 0; }全内存模式的优势与注意点xdb_load_content会先fseek到文件末尾确定文件大小再一次性读取整个文件到content-buffer见 xdb_util.cxdb_new_with_buffer创建的对象不再持有文件句柄查询过程中的所有读取向量索引、分段索引、region 数据都通过memcpy从内存缓冲完成io_count恒为 0见 xdb_searcher.c并发安全使用这种方式创建的查询对象可以安全用于并发。官方建议在服务启动时创建好然后一直安全并发使用直到服务关闭代价是常驻内存占用等于整个 xdb 文件大小适合对查询延迟敏感、内存充裕的场景。三种模式的取舍模式创建方式IO 特性并发安全典型场景filexdb_new_with_file_only每次查询多次磁盘 IOio_count大每线程独立 searcher内存敏感、低频查询vectorIndexxdb_new_with_vector_index向量索引命中内存分段索引读文件每线程独立 searcherv_index全局共享高并发查询的默认推荐contentxdb_new_with_buffer全程零磁盘 IOio_count为 0单对象可直接并发共享极致低延迟、内存充足七、region 定位信息的内存管理在旧版本实现中search 相关函数都依赖调用方指定一块region_buffer内存来存储地域信息这种方式有较大局限性。新实现提供了xdb_region_buffer_t对象来统一管理内存分配支持两种初始化方式// 1, 通过指定一块内存来创建 region_buffer char buffer[512]; xdb_region_buffer_t region; int err xdb_region_buffer_init(region, buffer, sizeof(buffer)); if (err ! 0) { // 初始化失败 printf(failed to init region buffer width errcode%d, err); return; } // 2通过指定 NULL 来创建 region_buffer让其自动按需分配内存 xdb_region_buffer_t region; int err xdb_region_buffer_init(region, NULL, 0); if (err ! 0) { // 初始化失败 printf(failed to init region buffer width errcode%d, err); return; } // 备注在每次调用 search 完成 IP 定位信息的查询后你需要手动调用函数来释放内存 . // search 函数使用未经清理的 region 信息会报错。 xdb_region_buffer_free(region);两种方式的选择依据指定固定 buffer适合地域信息最大长度已知的场景可减少运行过程中内存碎片的堆积。xdb_region_buffer_init在收到非空 buffer 时会将其memset清零并登记为 wrapper 类型见 xdb_searcher.c指定 NULL对象自动按需分配内存适合地域信息长度不确定、或程序不便提前分配一块内存的场景但长期运行必然增加内存碎片堆积。此时xdb_region_buffer_alloc会通过xdb_malloc(length 1)动态分配并自动补\0结尾见 xdb_searcher.c。关键纪律每次 search 调用完成 IP 定位查询后必须手动调用xdb_region_buffer_free释放内存search 函数使用未经清理的 region 信息会直接报错自动分配模式下xdb_region_buffer_alloc检测到value ! NULL会返回错误码 3。查询未命中时内部会调用xdb_region_buffer_empty写入空字符串见 xdb_searcher.c。八、编译构建 xdb_searcher 与 test_util在 c binding 根目录即 binding/c执行make即可编译得到两个可执行程序# cd 到 c binding 根目录 ➜ c git:(master) ✗ make gcc -stdc99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher gcc -stdc99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_utilMakefile 中还提供了静态库构建目标xdb_searcher_lib它会编译xdb_util.o与xdb_searcher.o打包为build/lib/libxdb.a并将头文件复制到build/include便于把 C 查询客户端集成进自己的项目make xdb_searcher_lib # 产出 build/lib/libxdb.a 与 build/include/xdb_api.h此外test_util是底层工具的自测程序支持按函数名调用例如./test_util test_load_header、./test_util test_parse_ip、./test_util test_ip_compare注册表见 test_util.c可用于验证头部加载、IP 解析与 IP 比较等基础能力。九、交互式查询测试search 命令通过xdb_searcher search命令测试对 xdb 的查询➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search ./xdb_searcher search [command options] options: --db string ip2region binary xdb file path --cache-policy string cache policy: file/vectorIndex/content例如使用默认的data/ip2region_v4.xdb进行 IPv4 查询测试➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db../../data/ip2region_v4.xdb ip2region xdb searcher test program source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex) type quit to exit ip2region 1.2.3.4 {region: Australia|Queensland|Brisbane|0|AU, io_count: 5, took: 39 μs} ip2region 120.229.45.2 {region: 中国|广东省|深圳市|移动|CN, io_count: 3, took: 13 μs}使用默认的data/ip2region_v6.xdb进行 IPv6 查询测试➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db../../data/ip2region_v6.xdb ip2region xdb searcher test program source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) type quit to exit ip2region :: {region: , io_count: 1, took: 38 μs} ip2region 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff {region: United States|Florida|Miami|velia.net Internetdienste GmbH|US, io_count: 14, took: 76 μs} ip2region 240e:3b7:3272:d8d0:db09:c067:8d59:539e {region: 中国|广东省|深圳市|电信|CN, io_count: 8, took: 42 μs}操作方式输入 IP 即可进行查询输入quit退出测试程序。也可以分别设置--cache-policy为file/vectorIndex/content来对比三种缓存实现的效率默认值为vectorIndex见 main.c。从输出可以观察到region字段按「国家|省份|城市|运营商|国家代码」的竖线分隔结构返回io_count反映本次查询的磁盘读取次数content 模式下应为 0took为单次查询耗时微秒。十、基准测试bench 命令通过xdb_searcher bench命令进行 bench 测试一方面确保查询程序和 xdb 文件没有错误另一方面通过大量查询得到平均查询性能➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench ./xdb_searcher bench [command options] options: --db string ip2region binary xdb file path --src string source ip text file path --cache-policy string cache policy: file/vectorIndex/content例如通过默认的data/ip2region_v4.xdb和data/ipv4_source.txt进行 IPv4 的 bench 测试➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db../../data/ip2region_v4.xdb --src../../data/ipv4_source.txt Bench finished, {cache_policy: vectorIndex, total: 1367686, took: 7.640s, cost: 5 μs/op}通过默认的data/ip2region_v6.xdb和data/ipv6_source.txt进行 IPv6 的 bench 测试➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db../../data/ip2region_v6.xdb --src../../data/ipv6_source.txt Bench finished, {cache_policy: vectorIndex, total: 34159862, took: 857.750s, cost: 24 μs/op}bench 的内部工作机制见 main.c逐行解析--src指定的源文件格式为起始IP|结束IP|地域信息三段以|分隔对每行数据的起始 IP 与结束 IP 各执行一次查询并与源文件中的期望 region 逐字节比对strcmp任何不一致都会立即报错退出——这正是「确保查询程序和 xdb 文件没有错误」的含义统计总查询次数total、总耗时took并计算单次平均耗时costμs/op。可以设置--cache-policy参数分别测试file/vectorIndex/content不同缓存实现机制的效率。注意bench 使用的 src 文件必须是生成对应 xdb 文件时使用的同一份源文件否则 region 比对会因数据不一致而失败。默认的 xdb 二进制文件正是由 data/ipv4_source.txt 与 data/ipv6_source.txt 生成的因此可直接使用。十一、底层原理一次查询发生了什么结合 xdb_searcher.c 的xdb_search实现一次查询的完整链路为版本校验ip_len必须与 searcher 声明的版本字节数一致否则返回-1向量索引定位取 IP 前两个字节计算偏移idx根据缓存模式从v_index-buffer、content-buffer或文件读取8 字节中取得分段索引块的起始指针s_ptr与结束指针e_ptr空段判定若s_ptr 0 || e_ptr 0说明该段源数据缺失直接返回空 region 字符串二分查找在[s_ptr, e_ptr]区间内按分段索引大小IPv4 为 14 字节、IPv6 为 38 字节执行二分查找利用版本专用的ip_compare比较函数逐字节比较 IP找到命中分段后读出data_len与data_ptr读取 region 数据通过xdb_region_buffer_alloc为 region 分配空间然后从data_ptr处读取data_len字节的地域信息。正是「向量索引 二分查找」的机制让 xdb 查询具备微秒级延迟而io_count也由此产生二分查找每次读取分段索引都会触发一次文件 IOcontent 模式除外命中数据后再读一次 region 内容。理解这条链路有助于你在集成时针对不同的并发与延迟要求选择正确的缓存模式。补充仓库还提供了 C、Go、Java、Python、Rust、JavaScript 等多种语言的查询客户端见 binding 目录C 客户端的 API 设计与其他语言实现保持一致掌握本文内容后即可触类旁通。若需生成自定义 xdb 文件可参考 maker 目录下的各语言制作工具。赞分享后端网络【免费下载链接】ip2regionIp2region is an offline IP-to-Region localization library and IP data management framework with both IPv4 and IPv6 supports, 10-microsecond level query efficiency, xdb search client for many programming languages项目地址https://gitcode.com/GitHub_Trending/ip/ip2region点击查看免费下载相关推荐ip2region.js 查询客户端完整指南IPv4/IPv6 xdb 查询、三种缓存模式与基准测试ip2region.js 查询客户端完整指南IPv4/IPv6 xdb 查询、三种缓存模式与基准测试 导读 本文基于 ip2region 仓库中 bindin后端网络ip2region Lua 查询客户端完全指南从 xdb 文件验证到三种缓存模式的实战用法ip2region Lua 查询客户端完全指南从 xdb 文件验证到三种缓存模式的实战用法 本篇技术指南以 ip2region 仓库中的纯 Lua 查询客户端后端网络ip2region C 查询客户端实战指南构建、搜索、基准测试与 xdb 文件生成ip2region C 查询客户端实战指南构建、搜索、基准测试与 xdb 文件生成 本篇技术指南以 binding/cpp/README.md https后端网络上一篇Remove-MS-Edge技术架构深度解析Windows系统组件清理最佳实践下一篇UnityLauncherPro终极Unity项目管理工具告别版本混乱创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考