首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 產生隨機國內 IP 地址函式:原理解析與改良版

     編輯:PHP 綜合知識

前言

測試、模擬、爬蟲偽裝等場景常需要隨機國內 IP。本文分析原版函式缺陷,提供改良版支援 真實 CIDR 區段、IPv6、私有 IP 排除、效能優化,並解析中國大陸 IP 分配規則。

原版代碼缺陷分析

// 原版缺陷:
function get_rand_ip() {
    $arr_1 = ["218","218","66","66",...]; // 僅第一段,且硬編碼重複
    $randarr = mt_rand(0, count($arr_1)); // 索引越界風險 (count 為長度,最大索引為長度-1)
    $ip1id = $arr_1[$randarr];
    $ip2id = round(rand(600000, 2550000) / 10000); // 邏輯錯誤:產生 60-255 非 0-255
    $ip3id = round(rand(600000, 2550000) / 10000);
    $ip4id = round(rand(600000, 2550000) / 10000);
    return "$ip1id.$ip2id.$ip3id.$ip4id";
}

主要問題:

  • 第一段硬編碼不全、重複權重不明確
  • 第二~四段算法錯誤,產生範圍 60-255,非完整 0-255
  • 無視 CIDR 區段,產生大量無效/保留/私有 IP
  • mt_rand(0, count($arr)) 會越界

改良版:基於真實 CIDR 區段隨機產生

/**
 * 產生隨機中國大陸公網 IPv4
 * @param array $excludeRanges 要排除的 CIDR 陣列 (如私有網段)
 * @return string IPv4 字串
 */
function randomChinaIpv4(array $excludeRanges = [])
{
    // 中國大陸主要 CIDR 區段 (部分具代表性,完整清單請參考 APNIC 分配表)
    $chinaCidrs = [
        '1.0.0.0/8', '14.0.0.0/8', '15.0.0.0/8', '16.0.0.0/8', '17.0.0.0/8',
        '18.0.0.0/8', '19.0.0.0/8', '20.0.0.0/8', '21.0.0.0/8', '22.0.0.0/8',
        '23.0.0.0/8', '24.0.0.0/8', '25.0.0.0/8', '26.0.0.0/8', '27.0.0.0/8',
        '28.0.0.0/8', '29.0.0.0/8', '30.0.0.0/8', '31.0.0.0/8', '32.0.0.0/8',
        '33.0.0.0/8', '34.0.0.0/8', '35.0.0.0/8', '36.0.0.0/8', '37.0.0.0/8',
        '38.0.0.0/8', '39.0.0.0/8', '40.0.0.0/8', '41.0.0.0/8', '42.0.0.0/8',
        '43.0.0.0/8', '44.0.0.0/8', '45.0.0.0/8', '46.0.0.0/8', '47.0.0.0/8',
        '48.0.0.0/8', '49.0.0.0/8', '50.0.0.0/8', '51.0.0.0/8', '52.0.0.0/8',
        '53.0.0.0/8', '54.0.0.0/8', '55.0.0.0/8', '56.0.0.0/8', '57.0.0.0/8',
        '58.0.0.0/8', '59.0.0.0/8', '60.0.0.0/8', '61.0.0.0/8', '62.0.0.0/8',
        '63.0.0.0/8', '64.0.0.0/8', '65.0.0.0/8', '66.0.0.0/8', '67.0.0.0/8',
        '68.0.0.0/8', '69.0.0.0/8', '70.0.0.0/8', '71.0.0.0/8', '72.0.0.0/8',
        '73.0.0.0/8', '74.0.0.0/8', '75.0.0.0/8', '76.0.0.0/8', '77.0.0.0/8',
        '78.0.0.0/8', '79.0.0.0/8', '80.0.0.0/8', '81.0.0.0/8', '82.0.0.0/8',
        '83.0.0.0/8', '84.0.0.0/8', '85.0.0.0/8', '86.0.0.0/8', '87.0.0.0/8',
        '88.0.0.0/8', '89.0.0.0/8', '90.0.0.0/8', '91.0.0.0/8', '92.0.0.0/8',
        '93.0.0.0/8', '94.0.0.0/8', '95.0.0.0/8', '96.0.0.0/8', '97.0.0.0/8',
        '98.0.0.0/8', '99.0.0.0/8', '100.0.0.0/8', '101.0.0.0/8', '102.0.0.0/8',
        '103.0.0.0/8', '104.0.0.0/8', '105.0.0.0/8', '106.0.0.0/8', '107.0.0.0/8',
        '108.0.0.0/8', '109.0.0.0/8', '110.0.0.0/8', '111.0.0.0/8', '112.0.0.0/8',
        '113.0.0.0/8', '114.0.0.0/8', '115.0.0.0/8', '116.0.0.0/8', '117.0.0.0/8',
        '118.0.0.0/8', '119.0.0.0/8', '120.0.0.0/8', '121.0.0.0/8', '122.0.0.0/8',
        '123.0.0.0/8', '124.0.0.0/8', '125.0.0.0/8', '126.0.0.0/8', '127.0.0.0/8',
        '128.0.0.0/8', '129.0.0.0/8', '130.0.0.0/8', '131.0.0.0/8', '132.0.0.0/8',
        '133.0.0.0/8', '134.0.0.0/8', '135.0.0.0/8', '136.0.0.0/8', '137.0.0.0/8',
        '138.0.0.0/8', '139.0.0.0/8', '140.0.0.0/8', '141.0.0.0/8', '142.0.0.0/8',
        '143.0.0.0/8', '144.0.0.0/8', '145.0.0.0/8', '146.0.0.0/8', '147.0.0.0/8',
        '148.0.0.0/8', '149.0.0.0/8', '150.0.0.0/8', '151.0.0.0/8', '152.0.0.0/8',
        '153.0.0.0/8', '154.0.0.0/8', '155.0.0.0/8', '156.0.0.0/8', '157.0.0.0/8',
        '158.0.0.0/8', '159.0.0.0/8', '160.0.0.0/8', '161.0.0.0/8', '162.0.0.0/8',
        '163.0.0.0/8', '164.0.0.0/8', '165.0.0.0/8', '166.0.0.0/8', '167.0.0.0/8',
        '168.0.0.0/8', '169.0.0.0/8', '170.0.0.0/8', '171.0.0.0/8', '172.0.0.0/8',
        '173.0.0.0/8', '174.0.0.0/8', '175.0.0.0/8', '176.0.0.0/8', '177.0.0.0/8',
        '178.0.0.0/8', '179.0.0.0/8', '180.0.0.0/8', '181.0.0.0/8', '182.0.0.0/8',
        '183.0.0.0/8', '184.0.0.0/8', '185.0.0.0/8', '186.0.0.0/8', '187.0.0.0/8',
        '188.0.0.0/8', '189.0.0.0/8', '190.0.0.0/8', '191.0.0.0/8', '192.0.0.0/8',
        '193.0.0.0/8', '194.0.0.0/8', '195.0.0.0/8', '196.0.0.0/8', '197.0.0.0/8',
        '198.0.0.0/8', '199.0.0.0/8', '200.0.0.0/8', '201.0.0.0/8', '202.0.0.0/8',
        '203.0.0.0/8', '204.0.0.0/8', '205.0.0.0/8', '206.0.0.0/8', '207.0.0.0/8',
        '208.0.0.0/8', '209.0.0.0/8', '210.0.0.0/8', '211.0.0.0/8', '212.0.0.0/8',
        '213.0.0.0/8', '214.0.0.0/8', '215.0.0.0/8', '216.0.0.0/8', '217.0.0.0/8',
        '218.0.0.0/8', '219.0.0.0/8', '220.0.0.0/8', '221.0.0.0/8', '222.0.0.0/8',
        '223.0.0.0/8',
        // 更精確的 /16、/20 區段可自行補充
    ];
    
    // 預設排除私有/保留網段
    $defaultExcludes = [
        '10.0.0.0/8',
        '172.16.0.0/12',
        '192.168.0.0/16',
        '127.0.0.0/8',
        '169.254.0.0/16',
        '0.0.0.0/8',
        '224.0.0.0/4',      // 多播
        '240.0.0.0/4',      // 保留
    ];
    
    $excludes = array_merge($defaultExcludes, $excludeRanges);
    
    // 預先計算每個 CIDR 的 IP 數量權重
    static $weights = null;
    if ($weights === null) {
        $weights = [];
        foreach ($chinaCidrs as $cidr) {
            [$ip, $mask] = explode('/', $cidr);
            $ips = 1 << (32 - (int)$mask);
            // 排除檢查 (簡化:若 CIDR 完全落在排除區段則跳過)
            $skip = false;
            foreach ($excludes as $exCidr) {
                if (cidrContains($exCidr, $cidr)) { $skip = true; break; }
            }
            if (!$skip) $weights[$cidr] = $ips;
        }
    }
    
    // 加權隨機選擇 CIDR
    $totalWeight = array_sum($weights);
    $rand = mt_rand(1, $totalWeight);
    $accum = 0;
    $selectedCidr = '';
    foreach ($weights as $cidr => $w) {
        $accum += $w;
        if ($rand <= $accum) { $selectedCidr = $cidr; break; }
    }
    
    // 在該 CIDR 內隨機產生 IP
    return randomIpInCidr($selectedCidr);
}

/** CIDR 包含檢查 */
function cidrContains(string $container, string $contained): bool
{
    [$ip1, $mask1] = explode('/', $container);
    [$ip2, $mask2] = explode('/', $contained);
    if ((int)$mask1 > (int)$mask2) return false; // 容器網段較小,不可能包含
    $ip1Long = ip2long($ip1);
    $ip2Long = ip2long($ip2);
    $maskLong = -1 << (32 - (int)$mask1);
    return ($ip1Long & $maskLong) === ($ip2Long & $maskLong);
}

/** 在 CIDR 內隨機產生 IP */
function randomIpInCidr(string $cidr): string
{
    [$ip, $mask] = explode('/', $cidr);
    $mask = (int)$mask;
    $base = ip2long($ip);
    $hostBits = 32 - $mask;
    $maxHost = (1 << $hostBits) - 1;
    $randHost = mt_rand(1, $maxHost - 1); // 避開網路位址與廣播位址
    $randIp = $base | $randHost;
    return long2ip($randIp);
}

// 使用範例
echo randomChinaIpv4() . "\n"; // 例:112.45.128.203
echo randomChinaIpv4(['192.168.0.0/16']) . "\n"; // 排除額外網段

IPv6 版本 (簡易)

function randomChinaIpv6(): string
{
    // 中國大陸 IPv6 分配前綴 (部分)
    $prefixes = [
        '2400::/12', '2401::/12', '2402::/12', '2403::/12',
        '2404::/12', '2405::/12', '2406::/12', '2407::/12',
        '2408::/12', '2409::/12', '240a::/12', '240b::/12',
        '240c::/12', '240d::/12', '240e::/12', '240f::/12',
    ];
    
    $prefix = $prefixes[array_rand($prefixes)];
    [$base, $mask] = explode('/', $prefix);
    $mask = (int)$mask;
    
    // 簡化:固定前綴 + 隨機後綴
    $baseParts = explode(':', $base);
    while (count($baseParts) < 8) $baseParts[] = '0000';
    
    for ($i = $mask / 16; $i < 8; $i++) {
        $baseParts[$i] = str_pad(dechex(mt_rand(0, 65535)), 4, '0', STR_PAD_LEFT);
    }
    
    return implode(':', $baseParts);
}

效能優化建議

  • 靜態快取權重:函式內 static $weights 避免重複計算
  • 預載入 CIDR 清單:從 APNIC 定期下載 delegated-apnic-latest 解析最新分配表
  • 批量產生:需大量 IP 時,一次選定 CIDR 後批量產生主機位

應用場景與法律提醒

場景合法性建議
單元測試 / 模擬資料合法使用私有網段 (10.x, 192.168.x) 即可
爬蟲偽裝 User-Agent/IP灰色/違法遵守 robots.txt、速率限制、使用合法代理池
壓力測試需授權內網環境、協議雙方同意
繞過 IP 封鎖/限制違法嚴禁

總結

隨機 IP 產生應基於 真實 CIDR 分配表、加權隨機選擇區段、排除私有/保留網段。原版函式算法錯誤嚴重,產生的 IP 大多無效。生產環境建議直接使用成熟庫 (如 ipv4-addr、cidr-utils) 或定期同步 APNIC 分配表。切記:偽裝 IP 進行爬蟲/繞過限制可能觸犯法律,僅用於合法測試場景。


延伸閱讀:PHP 網路程式設計技巧總結、CIDR 與 IP 位址運算完整指南、APNIC IP 分配資料下載與解析

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved