一,如何判断一个ip地址是否属于国内?

我们以前使用淘宝提供的一个api地址进行判断,但经常出现打不开的报错,

因为只需要判断是国内或国外,于是考虑自己搞一个简单的。

分配给国内的ip地址在apnic的官方网站上可以下载到,但不方便直接判断,

我写了一个demo,可以供大家来参考:

总体上分为三部分:

1,定时下载ip地址,保存到文本文件

2,  解析ip地址列表,保存到redis

3,  拿到一个ip时,从redis中取出ip地址段进行比较,如果在各个地址段范围内,表示是国内ip,否则是国外ip

项目地址:  https://github.com/liuhongdi/isipinchina

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,下载ip地址并保存到文本文件

1,downchinaip.sh

#!/bin/bash
#variables,定义用到的变量 ip_txt_path=/data/data/ipdata/china_ip.txt;
ip_url='http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest';
php_path=/usr/local/soft/php7/bin/php
script_path=/data/web/think_cmd/chinaip/putip2redis.php #mv old txt,每次下载前把旧的ip地址文件改名,删除也可以 cur_time=$(date +"%Y%m%d%H%M%S");
if [ -f ${ip_txt_path} ];then
mv ${ip_txt_path} ${ip_txt_path}_${cur_time};
fi #download 用curl下载,保存到我们所定义的文本文件中 /usr/bin/curl ${ip_url} | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' >${ip_txt_path} #parse 2 redis,用php脚本解析,保存到redis echo "begin parse ip\n";
${php_path} ${script_path}

2,变量的配置:

指定下载后保存到本地的ip地址段文件

ip_txt_path=/data/data/ipdata/china_ip.txt;

apnic官网的ip地址段下载url,如果此地址有变化时,需修改ip_url此变量

ip_url='http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest';

二进制的php文件的路径,此处应设置为自己服务器上php的安装路径

php_path=/usr/local/soft/php7/bin/php

putip2redis.php保存到的路径

script_path=/data/web/think_cmd/chinaip/putip2redis.php

三,解析ip地址列表,保存到redis

1,我们下载到的ip地址段形如:

36.40.0.0/13
36.48.0.0/15
36.51.0.0/16
36.56.0.0/13
36.96.0.0/11

我们要做两项处理:

1,转为整数段,形如:

603979776--603980799

以方便对接收到ip参数进行比较

2,整个ip地址段有8千多条

[root@blog ipdata]$ wc -l china_ip.txt
8489 china_ip.txt

每次查询比较8000多次一则没有必要,二则影响效率,

我们取ip地址的第一段,做为索引,例如;36

这样可以在查询时先比较第一段,

如果此索引不存在,则不再继续比较,

如果存在,取出此索引下面的所有整数段,看ip地址是否存在于这些范围内

因为只需比较相同的第一段下面的ip地址段,每次比较的数量减少到了平均不到100个,

对于速度提升有好处

3,putip2redis.php

<?php
/* 解析国内ip地址列表,以ip地址的第一段为索引,
保存到redis中的一个hash中 by 刘宏缔
2020.04.02 */
//------------------------------------------------settings
ini_set("display_errors","On");
error_reporting(E_ALL);
//------------------------------------------------constant
define("REDIS_SERVER", "127.0.0.1");
define("REDIS_PORT", "6379");
define("IP_FILE", "/data/data/ipdata/china_ip.txt");
define("IP_HASH_NAME", "china_ip_hash");
//------------------------------------------------link 4 redis
$redis_link = new \Redis();
$redis_link->connect(REDIS_SERVER,REDIS_PORT); //------------------------------------------------main
set_ip_list(IP_FILE);
//------------------------------------------------function
//处理所有的ip范围到redis
function set_ip_list($ip_file) {
//从文件中得到所有的国内ip
$arr_all = file($ip_file); //遍历,得到所有的第一段
$arr_first = array();
foreach ($arr_all as $k => $rangeone) {
$rangeone = trim($rangeone);
if ($rangeone == "") {
continue;
}
$first = explode(".", $rangeone);
if (isset($first[0]) && $first[0]!='') {
$arr_first[] = $first[0];
}
} //对所有的第一段去除重复
$arr_first = array_unique($arr_first); //得到线上hash的所有key
$arr_hkeys = hash_keys(IP_HASH_NAME); //如果一个线上已存在的key不再存在于新ip的第一段的数组中
//需要从线上hash中删除
if (is_array($arr_hkeys) && sizeof($arr_hkeys)>0) {
foreach($arr_hkeys as $k => $hkey_one) {
if (!in_array($hkey_one, $arr_first)) {
echo "will delete :".$hkey_one."\n";
hash_delete_hkey(IP_HASH_NAME,$hkey_one);
}
}
} //得到每个第一段下面对应的所有ip地址段,保存到redis
foreach ($arr_first as $k => $first) {
add_a_list_by_first($first,$arr_all);
} } //把所有的第一段为指定数字的ip,添加到redis
function add_a_list_by_first($first,$arr) { $arr_line = array();
foreach ($arr as $k => $rangeone) {
$rangeone = trim($rangeone);
$first_a = explode(".", $rangeone);
if (!isset($first_a[0]) || $first_a[0] == "") {
continue;
}
$cur_first = $first_a[0];
if ($cur_first == $first) { $line = get_line_by_rangeone($rangeone);
//echo "line:".$line."\n";
$arr_line[] = $line;
} else {
continue;
}
} if (sizeof($arr_line) >0) {
$key_name = $first;
hash_set(IP_HASH_NAME,$key_name,$arr_line);
}
} //得到一个ip地址段的起始范围
function get_line_by_rangeone($networkRange) {
$s = explode('/', $networkRange);
$network_start = (double) (sprintf("%u", ip2long($s[0])));
$network_len = pow(2, 32 - $s[1]);
$network_end = $network_start + $network_len - 1; $line = $network_start."--".$network_end;
return $line;
} //redis set 一个数组到hash
function hash_set($hash_name,$key_name,$arr_value){
global $redis_link;
$str_value = json_encode($arr_value);
$b = $redis_link->hset($hash_name, $key_name, $str_value);
} //返回redis hash中所有的key,注意只是key,如果value也返回会影响速度
function hash_keys($hash_name) {
global $redis_link;
$arr = $redis_link->hKeys($hash_name);
return $arr;
} //删除一个hash的hkey
function hash_delete_hkey($hash_name,$key_name) {
global $redis_link;
$redis_link->hdel($hash_name, $key_name);
} ?>

说明:需要配置的常量:

define("REDIS_SERVER", "127.0.0.1");       //redis服务器的ip
define("REDIS_PORT", "6379"); //redis服务器的port
define("IP_FILE", "/data/data/ipdata/china_ip.txt"); //下载保存到本地的ip地址段文件,注意和downchinaip.sh中保持一致
define("IP_HASH_NAME", "china_ip_hash"); //保存到redis中的hash的名字

四,查询一个ip是否属于国内ip地址

1,查询时需要把ip转为整数进行比较

2,isipinchina.php

<?php
/*
判断一个ip是否国内的ip
需要连接到redis服务器进行判断
by 刘宏缔
2020.04.01
*/
//------------------------------------------------settings
ini_set("display_errors","On");
error_reporting(E_ALL);
//------------------------------------------------constant
define("REDIS_SERVER", "127.0.0.1");
define("REDIS_PORT", "6379");
define("IP_HASH_NAME", "china_ip_hash");
//------------------------------------------------link 2 redis
$redis_link = new \Redis();
$redis_link->connect(REDIS_SERVER,REDIS_PORT);
//------------------------------------------------main $ip = "203.137.164.152";
$is_in = is_ip_in_china($ip);
echo "is_in:".$is_in.":\n";
if ($is_in == true) {
echo "china:\n";
} else {
echo "out china:\n";
} //------------------------------------------------function //判断一个ip是否属于china
function is_ip_in_china($ip) {
$ip = trim($ip);
$first_a = explode(".", $ip);
if (!isset($first_a[0]) || $first_a[0] == "") {
//ip有误,按国外算
return false;
}
$first = $first_a[0]; $arr_range = hash_get(IP_HASH_NAME,$first);
if (!is_array($arr_range) || sizeof($arr_range) == 0) {
return false;
}
if (is_ip_in_arr_range($ip,$arr_range) == true) {
return true;
} else {
return false;
}
} //判断一个ip是否属于ip的range数组
function is_ip_in_arr_range($ip,$arr_range) {
$ip_long = (double) (sprintf("%u", ip2long($ip)));
foreach ($arr_range as $k => $one) {
$one = trim($one);
$arr_one = explode("--", $one);
if (!isset($arr_one[0]) || !isset($arr_one[1])) {
continue;
}
$begin = $arr_one[0];
$end = $arr_one[1];
if ($ip_long >= $begin && $ip_long <= $end) {
return true;
}
}
return false;
} //得到一个hash中对应key的value
function hash_get($hash_name,$key_name){
global $redis_link;
$str = $redis_link->hget($hash_name, $key_name);
$arr = json_decode($str,true);
return $arr;
} ?>

说明:需要配置的常量:

define("REDIS_SERVER", "127.0.0.1");        //redis服务器的ip
define("REDIS_PORT", "6379"); //redis服务器的port
define("IP_HASH_NAME", "china_ip_hash"); //保存到redis中的hash的名字

3,查询时的功能,大家在使用时应该封装成一个可以添加到项目框架的类来应用

五,把bash脚本downchinaip.sh添加到crond,每天定时运行

[root@blog ~]# crontab -l
30 0 * * * sh /data/web/think_cmd/chinaip/downchinaip.sh >> /data/logs/cronlogs/downchinaiplogs.log 2>&1

六,php的版本:

[root@blog chinaip]$ /usr/local/soft/php7/bin/php --version
PHP 7.4.2 (cli) (built: Mar 5 2020 11:16:38) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

七,备注:

这个方案的执行效率还可以,判断一个ip一次不到半毫秒,

[root@blog ~]# /usr/local/soft/php7/bin/php /data/web/think_cmd/chinaip/isipinchina.php
耗时0.00036秒:
is_in:1:
china

大家如果更好更成熟的方案可以给我留言,感谢!

判断ip地址是属于国内还是国外的更多相关文章

  1. C# 判断ip地址是否正确

    最后要用一方法判断ip地址是否正确,直接用.Net现成的类,方法如下: string ipStr="192.168.222.333"; IPAddress ip; if(IPAdd ...

  2. js判断ip地址,子网掩码,网关的逻辑性检查

    因为要做静态地址配置的js校验,找了好多资料发现网上都是关于ip,mask的有效性检查,没有ip,submask,gateway的逻辑性判断,自己写下代码供需要的人参考. 普及下网关地址知识: 就是进 ...

  3. PHP判断ip地址是否合法

    1.获取真正ip地址 function get_ip(){ //判断服务器是否允许$_SERVER if(isset($_SERVER)){ if(isset($_SERVER[HTTP_X_FORW ...

  4. 判断IP地址是否在指定范围内的方法

    比如给定一个ip段:127.0.0.1 ~ 127.0.0.255,我们想判断一个给定的ip地址是否在此段内,可以先将ip地址转换成整数,然后整数比较大小就很容易了. 例如: 127.0.0.1 = ...

  5. Python面试题: 判断IP地址是否合法

    题目: 给出一个字符串, 判断其是否是是合法的IP(IPv4)地址 思路 将字符串按"."分割成4段得到一个列表 逐个判断列表中的字符串是否数字格式并且在0~255之间, 是在新列 ...

  6. 判断IP地址的合法性

    每台计算机都有独一无二的编号,称为ip地址,每个合法的ip地址由‘.’分隔开的4个数字组成,每个数字的取值范围为0--255 输入一个字符串,判断其是否为合法的IP地址,若是输出‘YES’,否则输出‘ ...

  7. 判断IP地址的类型

    #include <stdio.h> #include <stdlib.h> void main() { ]; int ip_addr; printf("请输入IP地 ...

  8. C#判断ip地址是否ping的通

    Ping pingSender = new Ping(); PingReply reply = pingSender.Send("127.0.0.1",120);//第一个参数为i ...

  9. 正则表达式判断ip地址

    html: <div class="configuration"><form action="" name="myformcon&q ...

随机推荐

  1. 关于while (~scanf("%d %d", &m, &n))的用法

    其功能是循环从输入流读入m和n,直到遇到EOF,有如下关系: while (~scanf("%d %d", &m, &n)) ↔ while (scanf(&quo ...

  2. 吴恩达《深度学习》-第一门课 (Neural Networks and Deep Learning)-第四周:深层神经网络(Deep Neural Networks)-课程笔记

    第四周:深层神经网络(Deep Neural Networks) 4.1 深层神经网络(Deep L-layer neural network) 有一些函数,只有非常深的神经网络能学会,而更浅的模型则 ...

  3. python的运算符及数据类型的转换

    python 目录 python 1.算术运算符 2.比较运算符 3.赋值运算符和复合赋值运算符 4.位运算符 5.逻辑运算符 6.成员运算符 7.身份运算符 8.常见的数据类型转换 1.算术运算符 ...

  4. Centos6.5 离线 Openssh 升级

    目录 OpenSSH 升级 一.基于 Dropbear 设置备用 ssh 服务器 二. Openssh 更新 2.1 ssh配置 备份 2.2 openssh 升级 Openssl 升级(由于Open ...

  5. Spring注解不生效原因总结

    在Spring的注解学习中发现使用(@Resource.@PostConstruct. @PreDestroy)这三个注解时不生效.使用@Resource发生空指针异常,说 明被注解对象没有被成功注入 ...

  6. web网站——apache和nginx对比02

    nginx介绍 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理 ...

  7. Spark Driver Program剖析

    SparkContext是通往Spark集群的唯一入口,是整个Application运行调度的核心. 一.Spark Driver Program Spark Driver Program(以下简称D ...

  8. 【Flutter 实战】大量复杂数据持久化

    老孟导读:上一篇文章讲解了 Android 和 iOS 的文件目录系统,此篇文章讲解如何使用 SQLite 保存数据. 欢迎大家投稿:http://laomengit.com/plan/Contrib ...

  9. springboot maven项目运行常见报错 及ajax请求报错

    如图所示 tomcat运行后直接停止,也不报错 原因:我的原因是controller路径配置重名或者service没有配置@Service 遇见这错找了好久问题,网上也搜不到,特此记录一下 问题2 a ...

  10. python实现随机复制若干个文件到新目录

    python实现随机复制若干个文件到新目录 1说明 1.1 目的 随机选择一个文件下的若干个文件,并将文件复制到新文件夹下 1.2 要求 需要将random_select_and_copy_file. ...