标签: ip

  • 内网IP

    内网IP是以下面几个段开头的IP.用户可以自己设置.
    常用的内网IP地址:
       10.x.x.x
       172.16.x.x至172.31.x.x
       192.168.x.x
       查看内网IP地址

  • 利用QQWry.Dat实现IP地址高效检索

    1、原文的类中构造函数已转成PHP5的__construct,析构函数也换掉了。

    2、调用:

    $ip=’65.55.109.119′;
    $idADDR=new IpLocation();
    print_r($idADDR->getlocation($ip));

    返回结果:

    /* getlocation($ip)返回ARRAY,如下:
    Array
    (
    [ip] => 65.55.109.119 //输入的IP
    [beginip] => 65.52.0.0
    [endip] => 65.55.255.255
    [country] => 美国
    [area] => Microsoft公司
    )
    */

    根据 LumaQQ 开发者文档中的纯真IP数据库格式详解,我编写了一个 PHP 的查询 IP 所在地区信息的类。在编写过程中发现纯真IP数据库格式详解中关于记录区的描述不是很全面,不过出入也不是很大,所以我没必要再写一份纯真 IP 数据库的格式说明了,大家感兴趣的话,读一读下面的代码应该就能看出来了。代码中加了很详细的注释,应该很容易读懂的。

    在创建这个类的一个实例后,实例中就保存了打开的文件指针和一些查询需要的信息,每次查询时不需要重新打开文件,直到页面执行结束后,打开的文件才会自动关闭。这样。在一个页面内进行多次查询时,效率是很高的。并且此类不仅可以直接查询 IP,还可以自动将域名解析为 IP 进行查询。

    下面是程序代码:

    <?php

    /**
    * IP 地理位置查询类
    *
    * @author 马秉尧
    * @version 1.5
    * @copyright 2005 CoolCode.CN
    */
    class IpLocation {
    /**
    * QQWry.Dat文件指针
    * @var resource
    */
    var $fp;

    /**
    * 第一条IP记录的偏移地址
    * @var int
    */
    var $firstip;

    /**
    * 最后一条IP记录的偏移地址
    * @var int
    */
    var $lastip;

    /**
    * IP记录的总条数(不包含版本信息记录)
    * @var int
    */
    var $totalip;

    /**
    * 构造函数,打开 QQWry.Dat 文件并初始化类中的信息
    * @param string $filename
    * @return IpLocation
    */
    function __construct($filename = “QQWry.Dat”) {
    $this->fp = 0;
    if (($this->fp = @fopen($filename, ‘rb’)) !== false) {
    $this->firstip = $this->getlong();
    $this->lastip = $this->getlong();
    $this->totalip = ($this->lastip – $this->firstip) / 7;
    //注册析构函数,使其在程序执行结束时执行
    register_shutdown_function(array(&$this, ‘__construct’));
    }
    }

    /**
    * 返回读取的长整型数
    * @access private
    * @return int
    */
    function getlong() {
    //将读取的little-endian编码的4个字节转化为长整型数
    $result = unpack(‘Vlong’, fread($this->fp, 4));
    return $result[‘long’];
    }

    /**
    * 返回读取的3个字节的长整型数
    *
    * @access private
    * @return int
    */
    function getlong3() {
    //将读取的little-endian编码的3个字节转化为长整型数
    $result = unpack(‘Vlong’, fread($this->fp, 3).chr(0));
    return $result[‘long’];
    }

    /**
    * 返回压缩后可进行比较的IP地址
    *
    * @access private
    * @param string $ip
    * @return string
    */
    function packip($ip) {
    // 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
    // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
    return pack(‘N’, intval(ip2long($ip)));
    }

    /**
    * 返回读取的字符串
    *
    * @access private
    * @param string $data
    * @return string
    */
    function getstring($data = “”) {
    $char = fread($this->fp, 1);
    while (ord($char) > 0) { // 字符串按照C格式保存,以\0结束
    $data .= $char; // 将读取的字符连接到给定字符串之后
    $char = fread($this->fp, 1);
    }
    return $data;
    }

    /**
    * 返回地区信息
    *
    * @access private
    * @return string
    */
    function getarea() {
    $byte = fread($this->fp, 1); // 标志字节
    switch (ord($byte)) {
    case 0: // 没有区域信息
    $area = “”;
    break;
    case 1:
    case 2: // 标志字节为1或2,表示区域信息被重定向
    fseek($this->fp, $this->getlong3());
    $area = $this->getstring();
    break;
    default: // 否则,表示区域信息没有被重定向
    $area = $this->getstring($byte);
    break;
    }
    return $area;
    }

    /**
    * 根据所给 IP 地址或域名返回所在地区信息
    * @access public
    * @param string $ip
    * @return array
    */
    function getlocation($ip) {
    if (!$this->fp) return null; // 如果数据文件没有被正确打开,则直接返回空
    $location[‘ip’] = gethostbyname($ip); // 将输入的域名转化为IP地址
    $ip = $this->packip($location[‘ip’]); // 将输入的IP地址转化为可比较的IP地址
    // 不合法的IP地址会被转化为255.255.255.255
    // 对分搜索
    $l = 0; // 搜索的下边界
    $u = $this->totalip; // 搜索的上边界
    $findip = $this->lastip; // 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
    while ($l <= $u) { // 当上边界小于下边界时,查找失败
    $i = floor(($l + $u) / 2); // 计算近似中间记录
    fseek($this->fp, $this->firstip + $i * 7);
    $beginip = strrev(fread($this->fp, 4)); // 获取中间记录的开始IP地址
    // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式
    // 以便用于比较,后面相同。
    if ($ip < $beginip) { // 用户的IP小于中间记录的开始IP地址时
    $u = $i – 1; // 将搜索的上边界修改为中间记录减一
    }
    else {
    fseek($this->fp, $this->getlong3());
    $endip = strrev(fread($this->fp, 4)); // 获取中间记录的结束IP地址
    if ($ip > $endip) { // 用户的IP大于中间记录的结束IP地址时
    $l = $i + 1; // 将搜索的下边界修改为中间记录加一
    }
    else { // 用户的IP在中间记录的IP范围内时
    $findip = $this->firstip + $i * 7;
    break; // 则表示找到结果,退出循环
    }
    }
    }

    //获取查找到的IP地理位置信息
    fseek($this->fp, $findip);
    $location[‘beginip’] = long2ip($this->getlong()); // 用户IP所在范围的开始地址
    $offset = $this->getlong3();
    fseek($this->fp, $offset);
    $location[‘endip’] = long2ip($this->getlong()); // 用户IP所在范围的结束地址
    $byte = fread($this->fp, 1); // 标志字节
    switch (ord($byte)) {
    case 1: // 标志字节为1,表示国家和区域信息都被同时重定向
    $countryOffset = $this->getlong3(); // 重定向地址
    fseek($this->fp, $countryOffset);
    $byte = fread($this->fp, 1); // 标志字节
    switch (ord($byte)) {
    case 2: // 标志字节为2,表示国家信息又被重定向
    fseek($this->fp, $this->getlong3());
    $location[‘country’] = $this->getstring();
    fseek($this->fp, $countryOffset + 4);
    $location[‘area’] = $this->getarea();
    break;
    default: // 否则,表示国家信息没有被重定向
    $location[‘country’] = $this->getstring($byte);
    $location[‘area’] = $this->getarea();
    break;
    }
    break;
    case 2: // 标志字节为2,表示国家信息被重定向
    fseek($this->fp, $this->getlong3());
    $location[‘country’] = $this->getstring();
    fseek($this->fp, $offset + 8);
    $location[‘area’] = $this->getarea();
    break;
    default: // 否则,表示国家信息没有被重定向
    $location[‘country’] = $this->getstring($byte);
    $location[‘area’] = $this->getarea();
    break;
    }
    if ($location[‘country’] == ” CZ88.NET”) { // CZ88.NET表示没有有效信息
    $location[‘country’] = “未知”;
    }
    if ($location[‘area’] == ” CZ88.NET”) {
    $location[‘area’] = “”;
    }
    return $location;
    }

    /**
    * 析构函数,用于在页面执行结束后自动关闭打开的文件。
    *
    */
    function __desctruct() {
    if ($this->fp) {
    fclose($this->fp);
    }
    $this->fp = 0;
    }
    }
    ?>

  • 500台PC以上的局域网配置IP、子网掩码

    IP和子网掩码

    我们都知道,IP是由四段数字组成,在此,我们先来了解一下3类常用的IP

      A类IP段  0.0.0.0 到127.255.255.255

      B类IP段  128.0.0.0 到191.255.255.255

      C类IP段  192.0.0.0 到223.255.255.255

      XP默认分配的子网掩码每段只有255或0

      A类的默认子网掩码 255.0.0.0     一个子网最多可以容纳1677万多台电脑

      B类的默认子网掩码 255.255.0.0    一个子网最多可以容纳6万台电脑

      C类的默认子网掩码 255.255.255.0   一个子网最多可以容纳254台电脑

      我以前认为,要想把一些电脑搞在同一网段,只要IP的前三段一样就可以了,今天,我才知道我错了。如果照我这说的话,一个子网就只能容纳254台电脑?真是有点笑话。我们来说详细看看吧。

      要想在同一网段,只要网络标识相同就可以了,那要怎么看网络标识呢?首先要做的是把每段的IP转换为二进制。(有人说,我不会转换耶,没关系,我们用Windows自带计算器就行。打开计算器,点查看>科学型,输入十进制的数字,再点一下“二进制”这个单选点,就可以切换至二进制了。)

      把子网掩码切换至二进制,我们会发现,所有的子网掩码是由一串[red]连续[/red]的1和一串[red]连续[/red]的0组成的(一共4段,每段8位,一共32位数)。

      255.0.0.0   11111111.00000000.00000000.00000000

      255.255.0.0  11111111.11111111.00000000.00000000

      255.255.255.0 11111111.11111111.11111111.00000000

      这是A/B/C三类默认子网掩码的二进制形式,其实,还有好多种子网掩码,只要是一串连续的1和一串连续的0就可以了(每段都是8位)。如11111111.11111111.11111000.00000000,这也是一段合法的子网掩码。子网掩码决定的是一个子网的计算机数目,计算机公式是2的m次方,其中,我们可以把m看到是后面的多少颗0。如255.255.255.0转换成二进制,那就是11111111.11111111.11111111.00000000,后面有8颗0,那m就是8,255.255.255.0这个子网掩码可以容纳2的8次方(台)电脑,也就是256台,但是有两个IP是不能用的,那就是最后一段不能为0和255,减去这两台,就是254台。我们再来做一个。

      255.255.248.0这个子网掩码可以最多容纳多少台电脑?

      计算方法:

      把将其转换为二进制的四段数字(每段要是8位,如果是0,可以写成8个0,也就是00000000)

      11111111.1111111.11111000.00000000

      然后,数数后面有几颗0,一共是有11颗,那就是2的11次方,等于2048,这个子网掩码最多可以容纳2048台电脑。

      一个子网最多可以容纳多少台电脑你会算了吧,下面我们来个逆向算法的题。

      一个公司有530台电脑,组成一个对等局域网,子网掩码设多少最合适?

      首先,无疑,530台电脑用B类IP最合适(A类不用说了,太多,C类又不够,肯定是B类),但是B类默认的子网掩码是255.255.0.0,可以容纳6万台电脑,显然不太合适,那子网掩码设多少合适呢?我们先来列个公式。

      2的m次方=560

      首先,我们确定2一定是大于8次方的,因为我们知道2的8次方是256,也就是C类IP的最大容纳电脑的数目,我们从9次方一个一个试2的9次方是512,不到560,2的10次方是1024,看来2的10次方最合适了。子网掩码一共由32位组成,已确定后面10位是0了,那前面的22位就是1,最合适的子网掩码就是:11111111.11111111.11111100.00000000,转换成10进制,那就是255.255.252.0。

      分配和计算子网掩码你会了吧,下面,我们来看看IP地址的网段。

      相信好多人都和偶一样,认为IP只要前三段相同,就是在同一网段了,其实,不是这样的,同样,我样把IP的每一段转换为一个二进制数,这里就拿IP:192.168.0.1,子网掩码:255.255.255.0做实验吧。

      192.168.0.1

      11000000.10101000.00000000.00000001

      (这里说明一下,和子网掩码一样,每段8位,不足8位的,前面加0补齐。)

      IP    11000000.10101000.00000000.00000001

      子网掩码  11111111.11111111.11111111.00000000

      在这里,向大家说一下到底怎么样才算同一网段。

      要想在同一网段,必需做到网络标识相同,那网络标识怎么算呢?各类IP的网络标识算法都是不一样的。A类的,只算第一段。B类,只算第一、二段。C类,算第一、二、三段。

      算法只要把IP和子网掩码的每位数AND就可以了。

      AND方法:0和1=0 0和0=0 1和1=1

      如:And 192.168.0.1,255.255.255.0,先转换为二进制,然后AND每一位

      IP      11000000.10101000.00000000.00000001

      子网掩码    11111111.11111111.11111111.00000000

      得出AND结果  11000000.10101000.00000000.00000000

      转换为十进制192.168.0.0,这就是网络标识,

      再将子网掩码反取,也就是00000000.00000000.00000000.11111111,与IP AND

      得出结果00000000.00000000.00000000.00000001,转换为10进制,即0.0.0.1,

      这0.0.0.1就是主机标识。要想在同一网段,必需做到网络标识一样。

      我们再来看看这个改为默认子网掩码的B类IP

      如IP:188.188.0.111,188.188.5.222,子网掩码都设为255.255.254.0,在同一网段吗?

      先将这些转换成二进制

      188.188.0.111 10111100.10111100.00000000.01101111

      188.188.5.222 10111100.10111100.00000101.11011010

      255.255.254.0 11111111.11111111.11111110.00000000

      分别AND,得

      10111100.10111100.00000000.00000000

      10111100.10111100.00000100.00000000

      网络标识不一样,即不在同一网段。

      判断是不是在同一网段,你会了吧,下面,我们来点实际的。

      一个公司有530台电脑,组成一个对等局域网,子网掩码和IP设多少最合适?

      子网掩码不说了,前面算出结果来了11111111.11111111.11111100.00000000,也就是255.255.252.0

      我们现在要确定的是IP如何分配,首先,选一个B类IP段,这里就选188.188.x.x吧

      这样,IP的前两段确定的,关键是要确定第三段,只要网络标识相同就可以了。我们先来确定网络号。(我们把子网掩码中的1和IP中的?对就起来,0和*对应起来,如下:)

      255.255.252.0 11111111.11111111.11111100.00000000

      188.188.x.x  10111100.10111100.??????**.********

      网络标识   10111100.10111100.??????00.00000000

      由此可知,?处随便填(只能用0和1填,不一定全是0和1),我们就用全填0吧,*处随便,这样呢,我们的IP就是

      10111100.10111100.000000**.********,一共有530台电脑,IP的最后一段1~254可以分给254台计算机,530/254=2.086,采用进1法,得整数3,这样,我们确定了IP的第三段要分成三个不同的数字,也就是说,把000000**中的**填三次数字,只能填1和0,而且每次的数字都不一样,至于填什么,就随我们便了,如00000001,00000010,00000011,转换成二进制,分别是1,2,3,这样,第三段也确定了,这样,就可以把IP分成188.188.1.y,188.188.2.y,188.188.3.y,y处随便填,只要在1~254范围之内,并且这530台电脑每台和每台的IP不一样,就可以了。

      有人也许会说,既然算法这么麻烦,干脆用A类IP和A类默认子网掩码得了,偶要告诉你的是,由于A类IP和A类默认子网掩码的主机数目过大,这样做无疑是大海捞针,如果同时局域网访问量过频繁、过大,会影响效率的,所以,最好设置符合自己的IP和子网掩码^_^