在编写服务器程序的时候,我们可能会对连接的客户端IP进行一些过滤、统计,...等等的一些操作。在此就需要一些技术手段来分析客户端的IP地址。总的来说可以有以下几种方法。

1、通过在线ip服务(使用post/get等技术手段)

投入少,但是查询速度慢,效率不高,对于并发量大的情况,效果不好。

2、通过离线ip查询

(1)使用付费版,投入大,效率高。

(2)使用免费版,限制大。

(3)使用开源版,效率高,但是准确率稍差。

结合上述几点看法,最终我选择开源离线IP查询方案。

就我本人认知来说,主要有两种方案:

1、选择纯真ip查询

纯真ip查询的数据库,是GBK编码,在utf8环境下会产生乱码,因此需要对纯真数据库进行转换,稍显麻烦,下面我会给出转换工具。

https://download.csdn.net/download/a13576560181/16231956

包装类代码

/*** 纯真离线IP查询*/
class CZ_IpQuery {
public:/*** 把整个文件读到一个字符串中* @param _file*/inline  explicit CZ_IpQuery(std::string _file) {std::ifstream ifs(_file, std::ios::binary);if (ifs.is_open()) {state = true;ifs.seekg(0, std::ios::end);size_t _fsz = (size_t) ifs.tellg();ifs.seekg(0, std::ios::beg);m_bytes.resize(_fsz);ifs.read(&m_bytes[0], (std::streamsize) _fsz);ifs.close();getHead();}}/*** 返回初始化状态* @return*/inline operator bool() {return state;}/*** 返回查询结果* @param _ip* @return*/inline std::tuple<bool, std::string, std::string> operator()(std::string &&_ip) {auto ip = getIP(std::move(_ip));if (ip != 0) {auto current = searchIP(m_index_head, m_index_tail, ip);auto[country, location] = getAddress(getValue(current + 4, 3));return std::make_tuple(true, country, location);} else {return std::make_tuple(false, "", "");}}private:/*** 获取指定的16进制串的值* @param start* @param length* @return*/inline unsigned long getValue(unsigned long start, int length) {unsigned long variable = 0;long val[length], i;for (i = 0; i < length; i++) {/*过滤高位,一次读取一个字符*/val[i] = m_bytes[start++] & 0x000000FF;}for (i = length - 1; i >= 0; i--) {/*因为读取多个16进制字符,叠加*/variable = variable * 0x100 + val[i];}return variable;}/*** 获取指定的字符串* @param start* @return*/inline std::string getString(unsigned long start) {char val;std::string string;/*读取字符串,直到遇到0x00为止*/do {val = m_bytes[start++];/*依次放入用来存储的字符串空间中*/string += val;} while (val != 0x00);return string;};/*** 读取指定IP的国家位置和地域位置* @param start* @return*/inline std::tuple<std::string, std::string> getAddress(unsigned long start) {unsigned long redirect_address, counrty_address, location_address;char val;std::string country, location;start += 4;/*读取首地址的值*/val = (m_bytes[start] & 0x000000FF);if (val == REDIRECT_MODE_1) {/*重定向1类型的*/redirect_address = getValue(start + 1, 3);/*混合类型,重定向1类型进入后遇到重定向2类型读取重定向后的内容,并设置地域位置的文件偏移量*/if ((m_bytes[redirect_address] & 0x000000FF) == REDIRECT_MODE_2) {counrty_address = getValue(redirect_address + 1, 3);location_address = redirect_address + 4;country = getString(counrty_address);}/*读取重定向1后的内容,并设置地域位置的文件偏移量*/else {counrty_address = redirect_address;country = getString(counrty_address);location_address = redirect_address + country.length();}}/*重定向2类型的*/else if (val == REDIRECT_MODE_2) {counrty_address = getValue(start + 1, 3);location_address = start + 4;country = getString(counrty_address);} else {counrty_address = start;country = getString(counrty_address);location_address = counrty_address + country.length();}/*读取地域位置*/if ((m_bytes[location_address] & 0x000000FF) == REDIRECT_MODE_2 ||(m_bytes[location_address + 1] & 0x000000FF) == REDIRECT_MODE_1) {location_address = getValue(location_address + 1, 3);}location = getString(location_address);return std::make_tuple(country, location);};/*** 读取索引部分的范围(在文件头中,最先的2个8位16进制)*/inline void getHead() {/*索引的起止位置的文件偏移量,存储在文件头中的前8个16进制中设置偏移量为0,读取4个字符*/m_index_head = getValue(0L, 4);/*索引的结束位置的文件偏移量,存储在文件头中的第8个到第15个的16进制中设置偏移量为4个字符,再读取4个字符*/m_index_tail = getValue(4L, 4);}/*** 搜索指定IP在索引区的位置,采用二分查找法;返回IP在索引区域的文件偏移量一条索引记录的结果是,前4个16进制表示起始IP地址后面3个16进制,表示该起始IP在IP信息段中的位置,文件偏移量* @param index_start* @param index_end* @param ip* @return*/inline unsigned long searchIP(unsigned long index_start,unsigned long index_end, unsigned long ip) {unsigned long index_current, index_top, index_bottom;unsigned long record;index_bottom = index_start;index_top = index_end;/*此处的7,是因为一条索引记录的长度是7*/index_current = ((index_top - index_bottom) / 7 / 2) * 7 + index_bottom;/*二分查找法*/do {record = getValue(index_current, 4);if (record > ip) {index_top = index_current;index_current = ((index_top - index_bottom) / 14) * 7 + index_bottom;} else {index_bottom = index_current;index_current = ((index_top - index_bottom) / 14) * 7 + index_bottom;}} while (index_bottom < index_current);/*返回关键字IP在索引区域的文件偏移量*/return index_current;}/*** 判断一个字符是否为数字字符,如果是,返回0 如果不是,返回1* @param c* @return*/inline bool beNumber(char c) {return (c >= '0' && c <= '9');}/*** 函数的参数是一个存储着IP地址的字符串首地址  返回该IP的16进制代码  如果输入的IP地址有错误,函数将返回0* @param ip_addr* @return*/inline unsigned long getIP(std::string &&ip_addr) {unsigned long ip = 0;int i, j = 0;/*依次读取字符串中的各个字符*/for (i = 0; i < ip_addr.length(); i++) {/*如果是IP地址间隔的‘.’符号把当前读取到的IP字段的值,存入ip变量中(注意,ip为叠加时,乘以16进制的0x100)并清除临时变量的值*/if (ip_addr[i] == '.') {ip = ip * 0x100 + j;j = 0;}/*往临时变量中写入当前读取到的IP字段中的字符值叠加乘以10,因为输入的IP地址是10进制*/else {/*判断,如果输入的IP地址不规范,不是10进制字符函数将返回0*/if (beNumber(ip_addr[i]))j = j * 10 + ip_addr[i] - '0';elsereturn 0;}}/*IP字段有4个,但是‘.’只有3个,叠加第四个字段值*/ip = ip * 0x100 + j;return ip;}private:bool state{false};std::string m_bytes;uint32_t m_index_head = 0;uint32_t m_index_tail = 0;
};

使用方法

CZ_IpQuery ipQuery("数据库路径");
auto[status, country, location] = ipQuery("待查询IP");
if (status) {;;;;
}

2、选择ip2region

项目地址:https://github.com/lionsoul2014/ip2region

下载项目后,需要对c绑定源代码进行下修改,在ip2region.h的头尾分别加上

#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
}
#endif

原因是,ip2region 的c绑定,是纯C语言写的,直接用c++调用会产生问题。

然后将 ip2region.h 和 ip2region.c添加到工程内。

包装类代码

/*** ip2region离线IP查询*/
class IP2_IpQuery {
public:using SearchFunc =  uint_t (*)(ip2region_t, const char *, datablock_t);enum class SearchType {Binary,Memory};explicit IP2_IpQuery(std::string &&dbFile, SearchType searchType = SearchType::Memory) {if (ip2region_create(&ip2rEntry, dbFile.c_str())) {switch (searchType) {case SearchType::Binary:func_ptr = ip2region_binary_search_string;status = true;return;case SearchType::Memory:func_ptr = ip2region_memory_search_string;status = true;return;default:func_ptr = nullptr;status = false;return;}}status = false;}~IP2_IpQuery(){ip2region_destroy(&ip2rEntry);}inline operator bool (){return status;}inline std::tuple<bool, int, std::string> operator() (std::string &&ip) {datablock_entry datablock;if (func_ptr != nullptr) {func_ptr(&ip2rEntry, ip.c_str(), &datablock);return std::make_tuple(true, datablock.city_id, datablock.region);} else {return std::make_tuple(false, -1, "");}}private:ip2region_entry ip2rEntry;SearchFunc func_ptr;bool status = false;
};

使用方法

IP2_IpQuery ipQuery("数据库路径");
auto[status, city_id, region] = ipQuery("待查询IP");
if (status) {;;;;
}

以上代码采用 c++17标准编写。

c++ IP地址离线查询相关推荐

  1. 国内外ip地址黑名单查询

    国内外ip地址黑名单查询 国内:[url]http://www.anti-spam.org.cn/Rbl/Query/Result[/url] 国外:[url]http://www.spamhaus. ...

  2. 分享淘宝的IP地址库查询接口

    以前用过腾讯和新浪的IP地址库查询接口,由于不能直接传递IP数据,所以只能从前端用JS获取用户地址信息.也用过纯真的IP数据库,为了保持数据准确,所以就得不断的更新这个数据库,稍微麻烦一些,这次分享一 ...

  3. Excel也可以根据ip地址在线查询归属地详细信息

    今天要和大家分享的是:Excel也可以根据ip地址在线查询归属地详细信息,先看下面的动图演示 ​ 1.先装好该插件,然后打开表格 ​ 2.然后选择ip地址,将获得国家.大区.省.市.县.运营商等详细信 ...

  4. 获取本机外网地址和ip地址所在地查询

    获取本机外网地址 站长之家 ( 返回格式: json) : http://ip.chinaz.com/getip.aspx 搜狐 ( 返回格式: js) : http://pv.sohu.com/ci ...

  5. API获取自己的公网IP地址 API查询IP信息

    异想之旅:本人原创博客完全手敲,绝对非搬运,全网不可能有重复:本人无团队,仅为技术爱好者进行分享,所有内容不牵扯广告.本人所有文章仅在CSDN和个人博客(一定是异想之旅域名)发布,除此之外全部是盗文! ...

  6. 输入域名或IP地址,查询同一IP地址上有哪些网站/域名:

      输入域名或IP地址,查询同一IP地址上有哪些网站/域名 http://www.114best.com/ip/114.aspx

  7. 99 网络编程_传统网络工程师如何利用python实现公司内网IP地址信息查询?

      网   工   圈 网络工程师阿龙圈内最早的公益公众号,本号已认证!学网络关注我一个就够了(关注近5w+)关注听说99%的网工都来这里充电吖关注我,一个老HCIE(编号3558)带你轻松玩网络技术 ...

  8. ip地址信息查询入口,附key

    分享一个查询ip地址信息的接口 http://apis.haoservice.com/lifeservice/queryIpAddr?ip=60.12.8.29&key=047c10b650e ...

  9. 关于centos7的ip地址突然查询不到的解决办法

    问题描述:刚开始我好好的改完配置文件能查询到ip地址,突然某一天我打开VMware,输入         ip addr 却查不到IP地址,导致我xshell也连不上,经过我苦寻答案,终于尝试通过,希 ...

最新文章

  1. 面试官:小伙汁,你画的SpringMVC请求处理过程是从网上抄的吧?
  2. POJ 1028: Web Navigation
  3. adf4351_在ADF实体PK属性中使用MySQL自动增量PK列
  4. 4.3 计算机网络之IPv4(IPv4分组、IPv4地址、NAT、子网划分与子网掩码、CIDR、ARP协议、DHCP、ICMP)
  5. 经典C语言程序100例之九五
  6. 线程池状态和使用注意点
  7. 中航工业集团金网络(北京)电子商务有限公司副总经理刘正珩:航空“智”造的供应链支撑平台...
  8. ASP.NET Core依赖注入解读amp;使用Autofac替代实现
  9. python安装好的界面_手把手教你配置最漂亮的PyCharm界面,Python程序员必备!
  10. (pytorch-深度学习系列)pytorch线性回归的便捷实现
  11. Python类的魔法方法
  12. 创建mysql制定字符集语句_创建数据库指定字符集语句
  13. python运算符解释_Python运算符的详细介绍
  14. java多线程使用心得
  15. AUTOCAD——圆命令
  16. MPB:西湖大学鞠峰组-​​微生物群落定量宏基因组和宏转录组
  17. TeamViewer使用心得
  18. Stripe中的 googlePay 和 applePay (Java)
  19. 【c++入门(2)】贪心训练
  20. vue直播流播放器DPlayer使用

热门文章

  1. 输入一个一维数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。
  2. Chrome for Mac键盘快捷键!
  3. SQL注入之floor报错注入
  4. linux学习lesson16
  5. java接收任意键继续_正确实现“按任意键继续”功能
  6. Git版本控制器(涵盖GitHub\Gitee码云\GitLab),全网最详细教程
  7. [RL 9] Trust Region Policy Optimization (ICML, 2015)
  8. ctags中−−−kinds=[+|−]kinds的使用
  9. website for all kinds of courses
  10. 天池小布助手对话短文本语义匹配-文本二分类实践(pytorch)