<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>嘟嘟鱼 &#187; qqwry</title>
	<atom:link href="http://www.duduyu.net/tag/qqwry/feed" rel="self" type="application/rss+xml" />
	<link>http://www.duduyu.net</link>
	<description>嘟嘟鱼的个人博客</description>
	<lastBuildDate>Thu, 15 Dec 2011 07:54:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>利用 QQWry.Dat 实现 IP 地址高效检索（PHP）</title>
		<link>http://www.duduyu.net/ip-address-to-achieve-efficient-use-of-qqwry-dat-retrieve-php</link>
		<comments>http://www.duduyu.net/ip-address-to-achieve-efficient-use-of-qqwry-dat-retrieve-php#comments</comments>
		<pubDate>Wed, 05 Dec 2007 15:10:00 +0000</pubDate>
		<dc:creator>嘟嘟鱼</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[ip]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[qqwry]]></category>

		<guid isPermaLink="false">http://www.kuaixiang.net/?p=113</guid>
		<description><![CDATA[根据 LumaQQ 开发者文档中的纯真 IP 数据库格式详解，我编写了一个 PHP 的查询 IP 所在地区信息的类。在编写过程中发现纯真 IP 数据库格式详解中关于记录区的描述不是很全面，不过出入也不是... ]]></description>
			<content:encoded><![CDATA[<p>根据 LumaQQ 开发者文档中的<a target="_blank" href="http://lumaqq.linuxsir.org/article/qqwry_format_detail.html">纯真 IP 数据库格式详解</a>，我编写了一个 PHP 的查询 IP 所在地区信息的类。在编写过程中发现纯真 IP 数据库格式详解中关于记录区的描述不是很全面，不过出入也不是很大，所以我没必要再写一份纯真 IP 数据库的格式说明了，大家感兴趣的话，读一读下面的代码应该就能看出来了。代码中加了很详细的注释，应该很容易读懂的。</p>
<p>在创建这个类的一个实例后，实例中就保存了打开的文件指针和一些查询需要的信息，每次查询时不需要重新打开文件，直到页面执行结束后，打开的文件才会自动关闭。这样。在一个页面内进行多次查询时，效率是很高的。并且此类不仅可以直接查询 IP，还可以自动将域名解析为 IP 进行查询。</p>
<p>下面是程序代码：<span id="more-291"></span></p>
<p>[codes=php]<!--p<-->/**<br />
* IP 地理位置查询类<br />
*<br />
* @author 马秉尧<br />
* @version 1.5<br />
* @copyright 2005 CoolCode.CN<br />
*/<br />
class IpLocation {<br />
    /**<br />
     * QQWry.Dat文件指针<br />
     *<br />
     * @var resource<br />
     */<br />
    var $fp;</p>
<p>    /**<br />
     * 第一条IP记录的偏移地址<br />
     *<br />
     * @var int<br />
     */<br />
    var $firstip;</p>
<p>    /**<br />
     * 最后一条IP记录的偏移地址<br />
     *<br />
     * @var int<br />
     */<br />
    var $lastip;</p>
<p>    /**<br />
     * IP记录的总条数（不包含版本信息记录）<br />
     *<br />
     * @var int<br />
     */<br />
    var $totalip;</p>
<p>    /**<br />
     * 返回读取的长整型数<br />
     *<br />
     * @access private<br />
     * @return int<br />
     */<br />
    function getlong() {<br />
        //将读取的little-endian编码的4个字节转化为长整型数<br />
        $result = unpack('Vlong', fread($this-&gt;fp, 4));<br />
        return $result['long'];<br />
    }</p>
<p>    /**<br />
     * 返回读取的3个字节的长整型数<br />
     *<br />
     * @access private<br />
     * @return int<br />
     */<br />
    function getlong3() {<br />
        //将读取的little-endian编码的3个字节转化为长整型数<br />
        $result = unpack('Vlong', fread($this-&gt;fp, 3).chr(0));<br />
        return $result['long'];<br />
    }</p>
<p>    /**<br />
     * 返回压缩后可进行比较的IP地址<br />
     *<br />
     * @access private<br />
     * @param string $ip<br />
     * @return string<br />
     */<br />
    function packip($ip) {<br />
        // 将IP地址转化为长整型数，如果在PHP5中，IP地址错误，则返回False，<br />
        // 这时intval将Flase转化为整数-1，之后压缩成big-endian编码的字符串<br />
        return pack('N', intval(ip2long($ip)));<br />
    }</p>
<p>    /**<br />
     * 返回读取的字符串<br />
     *<br />
     * @access private<br />
     * @param string $data<br />
     * @return string<br />
     */<br />
    function getstring($data = "") {<br />
        $char = fread($this-&gt;fp, 1);<br />
        while (ord($char) &gt; 0) {        // 字符串按照C格式保存，以\0结束<br />
            $data .= $char;             // 将读取的字符连接到给定字符串之后<br />
            $char = fread($this-&gt;fp, 1);<br />
        }<br />
        return $data;<br />
    }</p>
<p>    /**<br />
     * 返回地区信息<br />
     *<br />
     * @access private<br />
     * @return string<br />
     */<br />
    function getarea() {<br />
        $byte = fread($this-&gt;fp, 1);    // 标志字节<br />
        switch (ord($byte)) {<br />
            case 0:                     // 没有区域信息<br />
                $area = "";<br />
                break;<br />
            case 1:<br />
            case 2:                     // 标志字节为1或2，表示区域信息被重定向<br />
                fseek($this-&gt;fp, $this-&gt;getlong3());<br />
                $area = $this-&gt;getstring();<br />
                break;<br />
            default:                    // 否则，表示区域信息没有被重定向<br />
                $area = $this-&gt;getstring($byte);<br />
                break;<br />
        }<br />
        return $area;<br />
    }</p>
<p>    /**<br />
     * 根据所给 IP 地址或域名返回所在地区信息<br />
     *<br />
     * @access public<br />
     * @param string $ip<br />
     * @return array<br />
     */<br />
    function getlocation($ip) {<br />
        if (!$this-&gt;fp) return null;            // 如果数据文件没有被正确打开，则直接返回空<br />
        $location['ip'] = gethostbyname($ip);   // 将输入的域名转化为IP地址<br />
        $ip = $this-&gt;packip($location['ip']);   // 将输入的IP地址转化为可比较的IP地址<br />
                                                // 不合法的IP地址会被转化为255.255.255.255<br />
        // 对分搜索<br />
        $l = 0;                         // 搜索的下边界<br />
        $u = $this-&gt;totalip;            // 搜索的上边界<br />
        $findip = $this-&gt;lastip;        // 如果没有找到就返回最后一条IP记录（QQWry.Dat的版本信息）<br />
        while ($l &lt;= $u) {              // 当上边界小于下边界时，查找失败<br />
            $i = floor(($l + $u) / 2);  // 计算近似中间记录<br />
            fseek($this-&gt;fp, $this-&gt;firstip + $i * 7);<br />
            $beginip = strrev(fread($this-&gt;fp, 4));     // 获取中间记录的开始IP地址<br />
            // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式<br />
            // 以便用于比较，后面相同。<br />
            if ($ip &lt; $beginip) {       // 用户的IP小于中间记录的开始IP地址时<br />
                $u = $i - 1;            // 将搜索的上边界修改为中间记录减一<br />
            }<br />
            else {<br />
                fseek($this-&gt;fp, $this-&gt;getlong3());<br />
                $endip = strrev(fread($this-&gt;fp, 4));   // 获取中间记录的结束IP地址<br />
                if ($ip &gt; $endip) {     // 用户的IP大于中间记录的结束IP地址时<br />
                    $l = $i + 1;        // 将搜索的下边界修改为中间记录加一<br />
                }<br />
                else {                  // 用户的IP在中间记录的IP范围内时<br />
                    $findip = $this-&gt;firstip + $i * 7;<br />
                    break;              // 则表示找到结果，退出循环<br />
                }<br />
            }<br />
        }</p>
<p>        //获取查找到的IP地理位置信息<br />
        fseek($this-&gt;fp, $findip);<br />
        $location['beginip'] = long2ip($this-&gt;getlong());   // 用户IP所在范围的开始地址<br />
        $offset = $this-&gt;getlong3();<br />
        fseek($this-&gt;fp, $offset);<br />
        $location['endip'] = long2ip($this-&gt;getlong());     // 用户IP所在范围的结束地址<br />
        $byte = fread($this-&gt;fp, 1);    // 标志字节<br />
        switch (ord($byte)) {<br />
            case 1:                     // 标志字节为1，表示国家和区域信息都被同时重定向<br />
                $countryOffset = $this-&gt;getlong3();         // 重定向地址<br />
                fseek($this-&gt;fp, $countryOffset);<br />
                $byte = fread($this-&gt;fp, 1);    // 标志字节<br />
                switch (ord($byte)) {<br />
                    case 2:             // 标志字节为2，表示国家信息又被重定向<br />
                        fseek($this-&gt;fp, $this-&gt;getlong3());<br />
                        $location['country'] = $this-&gt;getstring();<br />
                        fseek($this-&gt;fp, $countryOffset + 4);<br />
                        $location['area'] = $this-&gt;getarea();<br />
                        break;<br />
                    default:            // 否则，表示国家信息没有被重定向<br />
                        $location['country'] = $this-&gt;getstring($byte);<br />
                        $location['area'] = $this-&gt;getarea();<br />
                        break;<br />
                }<br />
                break;<br />
            case 2:                     // 标志字节为2，表示国家信息被重定向<br />
                fseek($this-&gt;fp, $this-&gt;getlong3());<br />
                $location['country'] = $this-&gt;getstring();<br />
                fseek($this-&gt;fp, $offset + 8);<br />
                $location['area'] = $this-&gt;getarea();<br />
                break;<br />
            default:                    // 否则，表示国家信息没有被重定向<br />
                $location['country'] = $this-&gt;getstring($byte);<br />
                $location['area'] = $this-&gt;getarea();<br />
                break;<br />
        }<br />
        if ($location['country'] == " CZ88.NET") {  // CZ88.NET表示没有有效信息<br />
            $location['country'] = "未知";<br />
        }<br />
        if ($location['area'] == " CZ88.NET") {<br />
            $location['area'] = "";<br />
        }<br />
        return $location;<br />
    }</p>
<p>    /**<br />
     * 构造函数，打开 QQWry.Dat 文件并初始化类中的信息<br />
     *<br />
     * @param string $filename<br />
     * @return IpLocation<br />
     */<br />
    function IpLocation($filename = "QQWry.Dat") {<br />
        $this-&gt;fp = 0;<br />
        if (($this-&gt;fp = @fopen($filename, 'rb')) !== false) {<br />
            $this-&gt;firstip = $this-&gt;getlong();<br />
            $this-&gt;lastip = $this-&gt;getlong();<br />
            $this-&gt;totalip = ($this-&gt;lastip - $this-&gt;firstip) / 7;<br />
            //注册析构函数，使其在程序执行结束时执行<br />
            register_shutdown_function(array(&amp;$this, '_IpLocation'));<br />
        }<br />
    }</p>
<p>    /**<br />
     * 析构函数，用于在页面执行结束后自动关闭打开的文件。<br />
     *<br />
     */<br />
    function _IpLocation() {<br />
        if ($this-&gt;fp) {<br />
            fclose($this-&gt;fp);<br />
        }<br />
        $this-&gt;fp = 0;<br />
    }<br />
}<br />
?&gt;[/codes]</p>
<h3  class="related_post_title">您可能也对这些文章感兴趣</h3><ul class="related_post"><li><a href="http://www.duduyu.net/to-choose-u0026quotnextu0026quot-programming-language" title="选择学习“下一个”程序语言">选择学习“下一个”程序语言</a></li><li><a href="http://www.duduyu.net/use-php-to-download-css-file-picture" title="利用PHP下载CSS文件中的图片">利用PHP下载CSS文件中的图片</a></li><li><a href="http://www.duduyu.net/php-load-linux-under-way-mbstring" title="linux下php加载mbstring的方法">linux下php加载mbstring的方法</a></li><li><a href="http://www.duduyu.net/closing-today-in-the-server-124_115_0_x-of-ip" title="今天在服务器封了124.115.0.X的IP段">今天在服务器封了124.115.0.X的IP段</a></li><li><a href="http://www.duduyu.net/248" title="刚参加完Discuz！研发人员与PHPer技术交流会">刚参加完Discuz！研发人员与PHPer技术交流会</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.duduyu.net/ip-address-to-achieve-efficient-use-of-qqwry-dat-retrieve-php/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

