結合 PHP exec() 執行系統 arp -a 指令,解析輸出取得區域網裝置的 IP-MAC 對應表,再用 gethostbyaddr() 反解主機名。
= 224 && $lastOctet <= 239) continue; // 多播
if ($type !== '動態' && $type !== 'dynamic') continue; // 只保留動態分配
// 反解主機名(可能較慢,可選擇性啟用)
$hostname = @gethostbyaddr($ip);
if ($hostname === $ip) $hostname = '未知';
echo "IP: $ip | MAC: $mac | 主機名: $hostname\n";
}
}
function getArpCommand(): string
{
return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? 'arp -a' : 'arp -n';
}
function parseArpOutput(string $output): array
{
$devices = [];
$lines = explode("\n", $output);
$currentIf = null;
foreach ($lines as $line) {
$line = trim($line);
// 介面標題行
if (preg_match('/(?:Interface|介面)\s*[:\s]\s*(\d+\.\d+\.\d+\.\d+)/i', $line, $m)) {
$currentIf = $m[1];
continue;
}
// 資料行:IP MAC TYPE
// Windows: 192.168.1.50 00-11-22-33-44-55 動態
// Linux: 192.168.1.50 ether 00:11:22:33:44:55 C eth0
if (preg_match('/(\d+\.\d+\.\d+\.\d+)\s+([\da-fA-F:-]{17})/i', $line, $m)) {
$devices[] = [
'ip' => $m[1],
'mac' => str_replace('-', ':', $m[2]), // 統一用冒號
'interface' => $currentIf,
];
}
}
return $devices;
}
// 使用 pcntl_fork 或並行處理(需 CLI)
function scanSubnetParallel(string $subnet, int $concurrency = 50): Generator
{
$pids = [];
$results = [];
for ($i = 1; $i <= 254; $i++) {
$ip = "$subnet$i";
// 限制並發數
while (count($pids) >= $concurrency) {
foreach ($pids as $pid => $fp) {
if (feof($fp)) {
$results[$pid] = stream_get_contents($fp);
fclose($fp);
unset($pids[$pid]);
}
}
usleep(10000);
}
// 非同步 ping + arp
$cmd = "ping -n 1 -w 100 $ip >nul 2>&1 && arp -a $ip";
$fp = popen($cmd, 'r');
$pids[(int)$fp] = $fp;
}
// 收集剩餘
foreach ($pids as $pid => $fp) {
$results[$pid] = stream_get_contents($fp);
fclose($fp);
}
foreach ($results as $output) {
if (preg_match('/(\d+\.\d+\.\d+\.\d+)\s+([\da-fA-F-]{17})/i', $output, $m)) {
yield ['ip' => $m[1], 'mac' => $m[2]];
}
}
}
| 限制 | 說明 | 解決方案 |
|---|---|---|
| 需 CLI 執行 | Web 環境通常禁用 exec()、無 arp 權限 | CLI 排程寫入資料庫,Web 讀取資料庫 |
| arp 表不完整 | 只含近期通訊過的裝置 | 先發送廣播 ping 刷新鄰居表 |
| 防火牆阻擋 | ICMP/ARP 可能被封鎖 | 需網管配合開放 |
| 主機名解析慢 | gethostbyaddr() 會發送 DNS/NetBIOS 查詢 | 非同步解析、快取結果、或僅顯示 IP/MAC |
// 使用 php-snmp 擴充套件(需裝置支援 SNMP)
$session = new SNMP(SNMP::VERSION_2C, '192.168.1.1', 'public');
$interfaces = $session->walk('IF-MIB::ifDescr');
// 或呼叫 nmap(需安裝)
exec('nmap -sn 192.168.1.0/24 -oG -', $output);
// 解析 Grepable Output 格式