Discuz 5.0 中读取纯真IP数据库函数分析
Discuz 5.0 不在使用自己的IP数据,而是使用纯真IP的数据格式, 存取纯真IP数据库稍微有点麻烦,它的存储格式比较特殊也很有趣,具体的格式分析参考下面两个链接,其他语言实现参考文章末的链接。
《纯真IP数据库格式详解》
链接一:http://blog.csdn.net/heiyeshuwu/archive/2006/05/12/725675.aspx
链接二:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
纯真IP数据库官网:http://www.cz88.net/ip/
纯真IP数据库下载:http://update.cz88.net/soft/qqwry.rar
以下函数conrvertip()位于 Discuz!5_GBK/upload/include/misc.func.php 路径中,有兴趣可以具体去阅读分析。(下面代码我做了简单的修改,更便于阅读,核心没有修改)
<?
//
===================================
//
// 功能:IP地址获取真实地址函数
// 参数:$ip - IP地址
// 作者:[Discuz!] (C) Comsenz Inc.
//
//===================================
function
convertip(
$ip
) {
//
IP数据文件路径
$dat_path
=
'
QQWry.Dat
'
;
//
检查IP地址
if
(
!
preg_match
(
"
/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/
"
,
$ip
)) {
return
'
IP Address Error
'
;
}
//
打开IP数据文件
if
(
!
$fd
=
@
fopen
(
$dat_path
,
'
rb
'
)){
return
'
IP date file not exists or access denied
'
;
}
//
分解IP进行运算,得出整形数
$ip
=
explode
(
'
.
'
,
$ip
);
$ipNum
=
$ip
[
0
]
*
16777216
+
$ip
[
1
]
*
65536
+
$ip
[
2
]
*
256
+
$ip
[
3
];
//
获取IP数据索引开始和结束位置
$DataBegin
=
fread
(
$fd
,
4
);
$DataEnd
=
fread
(
$fd
,
4
);
$ipbegin
=
implode
(
''
,
unpack
(
'
L
'
,
$DataBegin
));
if
(
$ipbegin
<
0
)
$ipbegin
+=
pow
(
2
,
32
);
$ipend
=
implode
(
''
,
unpack
(
'
L
'
,
$DataEnd
));
if
(
$ipend
<
0
)
$ipend
+=
pow
(
2
,
32
);
$ipAllNum
=
(
$ipend
-
$ipbegin
)
/
7
+
1
;
$BeginNum
=
0
;
$EndNum
=
$ipAllNum
;
//
使用二分查找法从索引记录中搜索匹配的IP记录
while
(
$ip1num
>
$ipNum
||
$ip2num
<
$ipNum
) {
$Middle
=
intval
((
$EndNum
+
$BeginNum
)
/
2
);
//
偏移指针到索引位置读取4个字节
fseek
(
$fd
,
$ipbegin
+
7
*
$Middle
);
$ipData1
=
fread
(
$fd
,
4
);
if
(
strlen
(
$ipData1
)
<
4
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
//
提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
$ip1num
=
implode
(
''
,
unpack
(
'
L
'
,
$ipData1
));
if
(
$ip1num
<
0
)
$ip1num
+=
pow
(
2
,
32
);
//
提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
if
(
$ip1num
>
$ipNum
) {
$EndNum
=
$Middle
;
continue
;
}
//
取完上一个索引后取下一个索引
$DataSeek
=
fread
(
$fd
,
3
);
if
(
strlen
(
$DataSeek
)
<
3
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$DataSeek
=
implode
(
''
,
unpack
(
'
L
'
,
$DataSeek
.
chr
(
0
)));
fseek
(
$fd
,
$DataSeek
);
$ipData2
=
fread
(
$fd
,
4
);
if
(
strlen
(
$ipData2
)
<
4
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$ip2num
=
implode
(
''
,
unpack
(
'
L
'
,
$ipData2
));
if
(
$ip2num
<
0
)
$ip2num
+=
pow
(
2
,
32
);
//
没找到提示未知
if
(
$ip2num
<
$ipNum
) {
if
(
$Middle
==
$BeginNum
) {
fclose
(
$fd
);
return
'
Unknown
'
;
}
$BeginNum
=
$Middle
;
}
}
//
下面的代码读晕了,没读明白,有兴趣的慢慢读
$ipFlag
=
fread
(
$fd
,
1
);
if
(
$ipFlag
==
chr
(
1
)) {
$ipSeek
=
fread
(
$fd
,
3
);
if
(
strlen
(
$ipSeek
)
<
3
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$ipSeek
=
implode
(
''
,
unpack
(
'
L
'
,
$ipSeek
.
chr
(
0
)));
fseek
(
$fd
,
$ipSeek
);
$ipFlag
=
fread
(
$fd
,
1
);
}
if
(
$ipFlag
==
chr
(
2
)) {
$AddrSeek
=
fread
(
$fd
,
3
);
if
(
strlen
(
$AddrSeek
)
<
3
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$ipFlag
=
fread
(
$fd
,
1
);
if
(
$ipFlag
==
chr
(
2
)) {
$AddrSeek2
=
fread
(
$fd
,
3
);
if
(
strlen
(
$AddrSeek2
)
<
3
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$AddrSeek2
=
implode
(
''
,
unpack
(
'
L
'
,
$AddrSeek2
.
chr
(
0
)));
fseek
(
$fd
,
$AddrSeek2
);
}
else
{
fseek
(
$fd
,
-
1
,
SEEK_CUR);
}
while
((
$char
=
fread
(
$fd
,
1
))
!=
chr
(
0
))
$ipAddr2
.=
$char
;
$AddrSeek
=
implode
(
''
,
unpack
(
'
L
'
,
$AddrSeek
.
chr
(
0
)));
fseek
(
$fd
,
$AddrSeek
);
while
((
$char
=
fread
(
$fd
,
1
))
!=
chr
(
0
))
$ipAddr1
.=
$char
;
}
else
{
fseek
(
$fd
,
-
1
,
SEEK_CUR);
while
((
$char
=
fread
(
$fd
,
1
))
!=
chr
(
0
))
$ipAddr1
.=
$char
;
$ipFlag
=
fread
(
$fd
,
1
);
if
(
$ipFlag
==
chr
(
2
)) {
$AddrSeek2
=
fread
(
$fd
,
3
);
if
(
strlen
(
$AddrSeek2
)
<
3
) {
fclose
(
$fd
);
return
'
System Error
'
;
}
$AddrSeek2
=
implode
(
''
,
unpack
(
'
L
'
,
$AddrSeek2
.
chr
(
0
)));
fseek
(
$fd
,
$AddrSeek2
);
}
else
{
fseek
(
$fd
,
-
1
,
SEEK_CUR);
}
while
((
$char
=
fread
(
$fd
,
1
))
!=
chr
(
0
)){
$ipAddr2
.=
$char
;
}
}
fclose
(
$fd
);
//
最后做相应的替换操作后返回结果
if
(
preg_match
(
'
/http/i
'
,
$ipAddr2
)) {
$ipAddr2
=
''
;
}
$ipaddr
=
"
$ipAddr1 $ipAddr2
"
;
$ipaddr
=
preg_replace
(
'
/CZ88.NET/is
'
,
''
,
$ipaddr
);
$ipaddr
=
preg_replace
(
'
/^s*/is
'
,
''
,
$ipaddr
);
$ipaddr
=
preg_replace
(
'
/s*$/is
'
,
''
,
$ipaddr
);
if
(
preg_match
(
'
/http/i
'
,
$ipaddr
)
||
$ipaddr
==
''
) {
$ipaddr
=
'
Unknown
'
;
}
return
$ipaddr
;
}
//
========================
//
// 调用举例(速度很快)
//
//========================
echo
convertip(
'
219.238.235.10
'
);
//
输出: 北京市 电信通
echo
convertip(
'
23.56.82.12
'
);
//
输出:IANA
echo
convertip(
'
250.69.52.0
'
);
//
输出:IANA保留地址
echo
convertip(
'
238.69.52.0
'
);
//
输出:IANA保留地址 用于多点传送
echo
convertip(
'
192.168.0.1
'
);
//
输出:局域网 对方和您在同一内部网
echo
convertip(
'
255.255.255.255
'
);
//
输出:纯真网络 2006年11月20日IP数据
?>
附:(相应其他实现程序)
利用 QQWry.Dat 实现 IP 地址高效检索(PHP)(作者: andot)
纯真IP数据库(QQWry.Dat)查询 C源码 (作者:Windix)