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

PHP TCP 端口檢測類:Health 非阻塞連接實現

     編輯:PHP 綜合知識

核心原理

使用 socket_create + socket_set_nonblock + socket_connect + socket_select 實現帶超時的非阻塞連接,避免 fsockopen 無法精確控制連接超時的問題。

完整類代碼

<?php
class Health {
    public static $status;

    public function check($ip, $port) {
        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if (!$sock) return false;

        socket_set_nonblock($sock);
        @socket_connect($sock, $ip, $port); // 非阻塞下立即返回
        socket_set_block($sock);            // 恢復阻塞模式供 select 使用

        // select 監聽寫就緒(連接建立)或異常,超時 5 秒
        $r = [$sock]; $w = [$sock]; $e = [$sock];
        self::$status = socket_select($r, $w, $e, 5);

        socket_close($sock);
        return self::$status;
    }

    public function status() {
        switch (self::$status) {
            case 1: echo "Open\n"; break;   // 可寫 → 連接成功
            case 0: echo "Timeout\n"; break; // 超時
            default: echo "Closed/Error\n"; break; // 異常/拒絕
        }
    }
}

// 使用
$health = new Health();
$health->check('192.168.2.10', 80);
$health->status();

狀態碼含義

返回值含義socket_select 就緒數組
1端口開放(連接成功)$w 包含 socket
0超時(5 秒內無響應)三個數組均為空
-1 / false錯誤(創建失敗、權限等)—

進階:批量檢測與異步並發

function checkPorts(array $targets, $timeout=3) {
    $sockets = [];
    foreach ($targets as $i => [$ip, $port]) {
        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_nonblock($sock);
        @socket_connect($sock, $ip, $port);
        $sockets[$i] = ['sock'=>$sock, 'ip'=>$ip, 'port'=>$port];
    }

    $start = microtime(true);
    $results = [];
    while ($sockets && microtime(true) - $start < $timeout) {
        $r = []; $w = []; $e = [];
        foreach ($sockets as $i => $s) {
            $w[] = $s['sock']; $e[] = $s['sock'];
        }
        $n = @socket_select($r, $w, $e, 0, 500000); // 0.5 秒輪詢
        if ($n > 0) {
            foreach ($w as $sock) {
                foreach ($sockets as $i => $s) {
                    if ($s['sock'] === $sock) {
                        $results[$i] = ['ip'=>$s['ip'], 'port'=>$s['port'], 'status'=>'open'];
                        socket_close($sock);
                        unset($sockets[$i]);
                        break;
                    }
                }
            }
            foreach ($e as $sock) {
                foreach ($sockets as $i => $s) {
                    if ($s['sock'] === $sock && isset($sockets[$i])) {
                        $results[$i] = ['ip'=>$s['ip'], 'port'=>$s['port'], 'status'=>'error'];
                        socket_close($sock);
                        unset($sockets[$i]);
                        break;
                    }
                }
            }
        }
    }
    // 剩餘為 timeout
    foreach ($sockets as $i => $s) {
        $results[$i] = ['ip'=>$s['ip'], 'port'=>$s['port'], 'status'=>'timeout'];
        socket_close($s['sock']);
    }
    return $results;
}

應用場景

  • 服務可用性監控(HTTP/DB/Redis/MQ 端口)
  • 代理 IP 存活驗證
  • 內網服務發現與健康檢查
  • 負載均衡器後端節點探活

注意事項

  • 需開啟 sockets 擴展(PHP 編譯時 --enable-sockets)
  • Windows 下 socket_select 精度約 1ms,Linux 可達微秒
  • 大規模並發建議使用 Swoole/Workerman 等異步框架,原生 socket 受進程/文件描述符限制
  • 防火牆/安全組可能導致「超時」而非「拒絕」,需結合業務判斷
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved