IP转物理地址的原理(php版)

FW: http://bbs.chinaunix.net/thread-1218708-1-2.html

首先,IP对应的国家是比较好查的,ICANN及其它几个机构都有关于某个IP段所属国家和ISP信息,并且他们基本上都提供了免费查询。但难题在于每个洲的管理机构不同,且数据量庞大,不易收集。将IP对应到某个国家的某个ISP后还要了结到它是分配给哪个地区(城市)的,比如中国电信福建分公司的IP 段会分给厦门、福州等城市,而相对于这个就是比较难找了,可能ISP会有数据库会提供查询,可能也查不到,这些数据的来源就靠平时的积累、用户的贡献了。

当然,现在许多出名的数据库经过几年的发展已经比较完整和详细了,像免费的纯真数据库都有十几万条了,如过向国外的商业网站购买企业级应用的数据库,它的 准确度就有点惊人了。国外的 ip2location.com 提供的数据库就是如此,作为一个英文数据库,它除了对美国等地区的数据比较完整以外,甚至中国的数据的精确程度都不亚于国内的数据库。它还提供了IP对应的经纬度,如果搭配Google 提供的Google Earth 的API,就可以在网页上看到Google Earth了。

就保存的方法来看,一般有一个文件(如纯真的QQwry.dat)或数据库。在数据量庞大(如几十万条)的情况下,用文件保存明显优于数据库,这一点在后面会详解。无论采用哪种方式保存数据,它的一般格式为:

起点IP,终点IP,国家,地区
33996344,33996351,United Kingdom,XXX

某 些比较详细的数据库还有ISP,国家缩略名,对应邮编,甚至该地的经纬度!起点IP,终点IP不是我们常见的 XXX.XXX.XXX.XXX的形式,而是一个8-10位十进制数字构成的。这也是为了便于数据的搜索。该数据由IP转换而成,在PHP中有 ip2long() 函数可以转换,自己也可以写一个相同的函数

1. function dottedquad2long($ip){
2. $ip_arr = split(’\.’,$ip);
3. $iplong = (16777216 * $ip_arr[0]) + (65536 * $ip_arr[1]) + (256 * $ip_arr[2]) + $ip_arr[3];
4. return $iplong;
5. }

在一个文件的数据库中,通常每个数据间用某种间隔符间隔,当要查找一个IP如64.233.189.104,先将该IP转化为十进制数字 1089060200 ,再在文件中多次利用二分法查找该IP对应的区间,约通过10-20次查找,就可以准确地找到对应的位置了。

另外一种利用数据库保存数据的方法相比于文件的查找,PHP做的工作比较简单,但交给MySQL做的任务就比较艰巨了。现在一般的小型的数据库(如我找到的只有国家信息的数据),都有十万余条。要在那么多的数据库中查找一个区间,使用的时间倒不如用文件查找来得快。
关于查找数据在数据库和文件分别所用的时间我在上一篇文章中有提到过,一般来说用文件保存的数据查找速度明显优于数据库。

接下来就是我的完整的IP查询的方法了

数据库信息
数据库 ip 中表 ipdata 里面有:

id
startipnum 起点IP数字 如:33996344
endipnum终点IP数字 如:33996351
areaslug 国家/地区缩写 如:GB
area 国家全名 如: United Kingdom

1. <?php
2.
3. //调用的图片类型,默认gif
4. if($_GET[‘imgtype’] == ‘png’){
5. $imgtype = ‘png’;
6. }else{
7. $imgtype = ‘gif’;
8. }
9.
10. //分析请求(支持IP,主机查询)
11. if(!$_GET[‘s’]){
12. $ip = $_SERVER[“REMOTE_ADDR”];//无请求,查询访问者的IP信息
13. }else{
14. $s = trim(strtolower($_GET[‘s’]));//转换为小写
15. if(ereg(’[a-z]‘,$s) && substr($s ,-1,1) != ‘x’){
16. $ip = gethostbyname($s);//如果是主机,则转换为对应IP
17. }else{
18. if(substr($s ,-1,1) == ‘x’){
19. $ip = str_replace(’x’,’0′,$s);//为用户隐私考虑,允许最后一位用英文字符x取代。
20. }else{
21. $ip = $s;
22. }
23. }
24. }
25. $ip = long2ip(ip2long($ip));
26. $sip = sprintf(’%u’,ip2long($ip));
27. //再进一步分析整理出合法的IP地址,并转换为数字形式,便于在数据库中查找
28. $sql = “SELECT * FROM `ipdata` WHERE $sip >= `startipnum` AND $sip <= `endipnum`”;
29. //组织SQL在数据库中查找IP对应的区间
30. $link = mysql_connect(”localhost”, “root”, “”);
31. mysql_select_db(’ip’, $link);
32. $result = mysql_query($sql);
33. while ($row = mysql_fetch_array($result)) {
34. $return = $row;
35. }
36. //进行查询,得出结果
37. if(!$return[‘areaslug’] || $return[‘areaslug’] == ‘-h’){//没有结果或结果位置,返回问号的图标
38. header(’Content-type: image/png’);
39. readfile(’icon/png/other.png’);
40. }else{//根据调用格式不同分别调用对应的图标
41. if($imgtype == ‘png’){
42. header(’Content-type: image/png’);
43. readfile(’icon/png/’.strtolower($return[‘areaslug’]).’.png’);
44. }else{
45. header(’Content-type: image/gif’);
46. readfile(’icon/gif/’.strtolower($return[‘areaslug’]).’.gif’);
47. }
48. }
49.
50. ?>

Leave a Reply