【问题标题】:Detecting an IPv6 address in PHP and storing it properly in MySQL. How?在 PHP 中检测 IPv6 地址并将其正确存储在 MySQL 中。如何?
【发布时间】:2011-09-08 17:19:32
【问题描述】:

我阅读了一些已经提出的问题,我发现这很有用,虽然我没有尝试过Working with IPv6 Addresses in PHP

不过,假设我在 MySQL 中有一个“禁止”表。我将如何存储 IPv6 地址?该方法必须是通用的,即该字段必须能够包含 ipv4 或 ipv6 地址。这也必须适用于我的用户表中的 ip_addr 字段。

我通常会检查if(getip == $bans['ip']) { do something } 但是我的 getip 函数是针对 ipv4 afaik 的,我想知道它是否可以工作。

我使用的函数是

function getip()
{
    if(isset($_SERVER['REMOTE_ADDR']))
    {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    if(preg_match_all("#[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}#s", $_SERVER['HTTP_X_FORWARDED_FOR'], $addresses))
    {
        foreach($addresses[0] as $key => $val)
        {
            if(!preg_match("#^(10|172\.16|192\.168)\.#", $val))
            {
                $ip = $val;
                break;
            }
        }
    }
}

if(!isset($ip))
{
    if(isset($_SERVER['HTTP_CLIENT_IP']))
    {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    }
    else
    {
        $ip = '';
    }
}

$ip = preg_replace("#([^.0-9 ]*)#", "", $ip);
return $ip;
}

【问题讨论】:

    标签: php ipv6


    【解决方案1】:

    我最近在 mysql 中寻找用于存储 ipv6 地址的最佳数据类型。我没有找到使用 VARCHAR() 的充分理由,只有 使用的充分理由。

    我对 BINARY(16)、两种 BIGINT UNSIGNED 和 DECIMAL 数据类型进行了一些性能测试。

    我创建了以下表格,其中填充了来自 100 个随机网络的 2,000,000 个随机 IP 地址。

    CREATE TABLE ipv6_address_binary (
        id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
        addr BINARY(16) NOT NULL UNIQUE
    );
    
    CREATE TABLE ipv6_address_twobigints (
        id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
        haddr BIGINT UNSIGNED NOT NULL,
        laddr BIGINT UNSIGNED NOT NULL,
        UNIQUE uidx (haddr, laddr)
    );
    
    CREATE TABLE ipv6_address_decimal (
        id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
        addr DECIMAL(39,0) NOT NULL UNIQUE
    );
    

    然后我选择每个网络的所有 ip 地址并记录响应时间。 twobigints 表的平均响应时间约为 1 秒,而二进制表的平均响应时间约为百分之一秒。

    这里是查询。

    注意:

    X_[HIGH/LOW] 是 X 的最高/最低有效 64 位

    当 NETMASK_LOW 为 0 时,AND 条件被省略,因为它总是产生 true。对性能影响不大。

    SELECT COUNT(*) FROM ipv6_address_twobigints
    WHERE haddr & NETMASK_HIGH = NETWORK_HIGH
    AND laddr & NETMASK_LOW = NETWORK_LOW
    
    SELECT COUNT(*) FROM ipv6_address_binary
    WHERE addr >= NETWORK
    AND addr <= BROADCAST
    
    SELECT COUNT(*) FROM ipv6_address_decimal
    WHERE addr >= NETWORK
    AND addr <= BROADCAST
    

    平均响应时间:

    BINARY_InnoDB  0.0119529819489
    BINARY_MyISAM  0.0139244818687
    DECIMAL_InnoDB 0.017379629612
    DECIMAL_MyISAM 0.0179929423332
    BIGINT_InnoDB  0.782350552082
    BIGINT_MyISAM  1.07809265852
    

    【讨论】:

      【解决方案2】:

      不过,假设我在 MySQL 中有一个“禁止”表。我将如何存储 IPv6 地址?

      您可以将其存储在一个简单的 VARCHAR(40) 列中。
      考虑到示例 IPv6 最大值为 40 字节:

      2001:0DB8:0000:0000:0000:0000:1428:57ab 
      

      该列也将能够包含 IPv4

      【讨论】:

      • 您忘记了 IPv6 地址可以以较短的格式存储。示例:fe80:0000:0000:0000:0202:b3ff:fe1e:8329 可能是 fe80:0:0:0:202:b3ff:fe1e:8329。这将不同于数据库中的记录
      • @dikidera:为什么不以规范的方式存储它并与规范的方式进行比较?
      • @dikidera:没错。此时在 mysql 中使用它们之前只需将 :0: 替换为 :0000: 即可。
      • 在维基上它说,短版本也可以变得更短,即 fe80:0:0:0:202:b3ff:fe1e:8329 可以变成 fe80::202:b3ff:fe1e:8329所以这也行不通。我还需要一些工作功能,因为这对我来说是第一次。
      • @yes123:不,它没有。它的意思是“一串具有所需长度的零”。 ::1(环回)not 表示:0000:1,表示0000:0000:0000:0000:0000:0000:0000:0001,而2001:db8::1428:57ab 表示2001:0db8:0000:0000:0000:0000:1428:57ab
      猜你喜欢
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      • 2014-05-03
      • 2010-10-01
      • 2012-07-19
      • 2016-09-18
      相关资源
      最近更新 更多