判断ip地址是属于国内还是国外
一,如何判断一个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地址是属于国内还是国外的更多相关文章
- C# 判断ip地址是否正确
最后要用一方法判断ip地址是否正确,直接用.Net现成的类,方法如下: string ipStr="192.168.222.333"; IPAddress ip; if(IPAdd ...
- js判断ip地址,子网掩码,网关的逻辑性检查
因为要做静态地址配置的js校验,找了好多资料发现网上都是关于ip,mask的有效性检查,没有ip,submask,gateway的逻辑性判断,自己写下代码供需要的人参考. 普及下网关地址知识: 就是进 ...
- PHP判断ip地址是否合法
1.获取真正ip地址 function get_ip(){ //判断服务器是否允许$_SERVER if(isset($_SERVER)){ if(isset($_SERVER[HTTP_X_FORW ...
- 判断IP地址是否在指定范围内的方法
比如给定一个ip段:127.0.0.1 ~ 127.0.0.255,我们想判断一个给定的ip地址是否在此段内,可以先将ip地址转换成整数,然后整数比较大小就很容易了. 例如: 127.0.0.1 = ...
- Python面试题: 判断IP地址是否合法
题目: 给出一个字符串, 判断其是否是是合法的IP(IPv4)地址 思路 将字符串按"."分割成4段得到一个列表 逐个判断列表中的字符串是否数字格式并且在0~255之间, 是在新列 ...
- 判断IP地址的合法性
每台计算机都有独一无二的编号,称为ip地址,每个合法的ip地址由‘.’分隔开的4个数字组成,每个数字的取值范围为0--255 输入一个字符串,判断其是否为合法的IP地址,若是输出‘YES’,否则输出‘ ...
- 判断IP地址的类型
#include <stdio.h> #include <stdlib.h> void main() { ]; int ip_addr; printf("请输入IP地 ...
- C#判断ip地址是否ping的通
Ping pingSender = new Ping(); PingReply reply = pingSender.Send("127.0.0.1",120);//第一个参数为i ...
- 正则表达式判断ip地址
html: <div class="configuration"><form action="" name="myformcon&q ...
随机推荐
- spring boot 源码之SpringApplication
run方法 run方法主要创建和初始化ConfigurableApplicationContext,在各个节点调用SpringApplicationRunListener的回调函数,在发送异常时调用用 ...
- 【Unity 插件】Lean Pool 使用
2020-09-11 Lean Pool 是一个轻量级的游戏对象池库,它可以轻松的帮助你的游戏提高性能.当然,Lean Pool也可以工作于普通的C#类. 目录: 1.Lean Pool使用 2.Le ...
- 从零搭建Spring Boot脚手架(7):Elasticsearch应该独立服务
1. Spring Data Elasticsearch Spring Data Elasticsearch是Spring Data项目的子项目,提供了Elasticsearch与Spring的集成. ...
- nginx+tomcat集群方法
下载地址:wget http://nginx.org/download/nginx-1.16.1.tar.gz 解压:tar -zxvf 预编译 nginx+tomcat集群方法: 进入nginx配置 ...
- 别人写的很好Arduino教材
原文来自:https://www.arduino.cn/thread-31720-1-1.html 上一篇:Arduino教程--通过 库管理器 添加库 http://www.arduino.cn/t ...
- apt-get 安装软件时出现:“文件尺寸不符” 问题
报错信息 命中:1 http://packages.deepin.com/deepin panda InRelease 命中:2 http://linux.teamviewer.com/deb sta ...
- Codeforces Global Round 11 个人题解(B题)
Codeforces Global Round 11 1427A. Avoiding Zero 题目链接:click here 待补 1427B. Chess Cheater 题目链接:click h ...
- jquery购物车全选,取消全选,计算总金额
这是html代码 <div class="gwcxqbj"> <div class="gwcxd center"> <div cl ...
- JVM性能调优(3) —— 内存分配和垃圾回收调优
前序文章: JVM性能调优(1) -- JVM内存模型和类加载运行机制 JVM性能调优(2) -- 垃圾回收器和回收策略 一.内存调优的目标 新生代的垃圾回收是比较简单的,Eden区满了无法分配新对象 ...
- java安全编码指南之:锁的双重检测
目录 简介 单例模式的延迟加载 double check模式 静态域的实现 ThreadLocal版本 简介 双重检测锁定模式是一种设计模式,我们通过首次检测锁定条件而不是实际获得锁从而减少获取锁的开 ...