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

PHP fsockopen 實現 Socket 連接完全指南:HTTP 模擬、郵件發送、服務探測與非阻塞模式

     編輯:PHP 相關知識

函數原型

resource fsockopen ( string $hostname, int $port [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") ]]] )

返回文件指針資源,可用 fgets、fputs、fclose、feof 等文件函數操作。

參數詳解

參數說明
$hostname主機名/IP。Unix Socket 時為文件路徑
$port端口號。Unix Socket 時為 0
$errno引用參數,返回系統級錯誤號
$errstr引用參數,返回錯誤描述字符串
$timeout連接超時秒數 (浮點數支持毫秒)。默認 default_socket_timeout (通常 60s)

核心範例:模擬 HTTP GET 請求

<?php
$host = 'php.wilson.gs';
$port = 80;
$timeout = 10;

$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) {
    die("連接失敗: $errstr ($errno)\n");
}

// 發送 HTTP/1.0 請求 (注意雙換行結尾)
$request = "GET / HTTP/1.0\r\nHost: $host\r\n\r\n";
fputs($fp, $request);

// 讀取響應
$response = '';
while (!feof($fp)) {
    $response .= fgets($fp, 1024);
}
fclose($fp);

echo $response;
// 輸出完整 HTTP 響應 (頭 + 體)
?>

進階場景實戰

1. HTTP/1.1 長連接與分塊傳輸

<?php
function http_request($host, $path = '/', $method = 'GET', $headers = [], $body = '') {
    $fp = fsockopen($host, 80, $errno, $errstr, 10);
    if (!$fp) return false;
    
    $request = "$method $path HTTP/1.1\r\n";
    $request .= "Host: $host\r\n";
    $request .= "Connection: close\r\n";  // 簡單斷開,避免分塊解析
    foreach ($headers as $k => $v) {
        $request .= "$k: $v\r\n";
    }
    if ($body) {
        $request .= "Content-Length: " . strlen($body) . "\r\n";
    }
    $request .= "\r\n$body";
    
    fputs($fp, $request);
    $response = '';
    while (!feof($fp)) $response .= fgets($fp, 4096);
    fclose($fp);
    return $response;
}

// 使用
$resp = http_request('httpbin.org', '/post', 'POST', ['Content-Type' => 'application/json'], '{"a":1}');
?>

2. SMTP 郵件發送 (基礎演示)

<?php
function smtp_send($host, $from, $to, $subject, $body) {
    $fp = fsockopen($host, 25, $errno, $errstr, 10);
    if (!$fp) return false;
    
    $read = function() use ($fp) { return fgets($fp, 512); };
    $write = function($cmd) use ($fp) { fputs($fp, $cmd . "\r\n"); };
    
    $read();           // 220 歡迎
    $write("HELO localhost"); $read();
    $write("MAIL FROM:<$from>"); $read();
    $write("RCPT TO:<$to>"); $read();
    $write("DATA"); $read();
    $write("From: $from\r\nTo: $to\r\nSubject: $subject\r\n\r\n$body\r\n.");
    $read();           // 250 成功
    $write("QUIT"); $read();
    fclose($fp);
    return true;
}
// 注意:生產環境請用 PHPMailer/Symfony Mailer,支持 TLS、認證、MIME、佇列
?>

3. 服務端口探測 / 健康檢查

<?php
function check_port($host, $port, $timeout = 2) {
    $start = microtime(true);
    $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
    $elapsed = (microtime(true) - $start) * 1000;
    
    if ($fp) {
        fclose($fp);
        return ['status' => 'open', 'latency_ms' => round($elapsed, 2)];
    }
    return ['status' => 'closed', 'error' => "$errstr ($errno)", 'latency_ms' => round($elapsed, 2)];
}

// 批量檢查
$services = [
    ['localhost', 3306, 'MySQL'],
    ['localhost', 6379, 'Redis'],
    ['localhost', 9200, 'Elasticsearch'],
];
foreach ($services as [$h, $p, $n]) {
    $r = check_port($h, $p);
    echo "$n ($h:$p): {$r['status']} {$r['latency_ms']}ms\n";
}
?>

Unix Domain Socket (IPC)

<?php
// 連接本地 Unix Socket (如 PHP-FPM、Redis、MySQL socket)
$socket = '/var/run/php/php8.1-fpm.sock';
$fp = fsockopen("unix://$socket", 0, $errno, $errstr, 5);
if ($fp) {
    // FastCGI 協議交互...
    fclose($fp);
}
?>

非阻塞模式:stream_set_blocking

<?php
$fp = fsockopen('example.com', 80, $errno, $errstr, 5);
if ($fp) {
    // 設置非阻塞
    stream_set_blocking($fp, false);
    
    // 發送請求
    fputs($fp, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
    
    // 異步讀取 (需配合 stream_select 避免忙等)
    $read = [$fp];
    $write = $except = null;
    if (stream_select($read, $write, $except, 5)) {  // 5秒超時
        $data = fread($fp, 8192);
        // 處理數據...
    }
    fclose($fp);
}
?>

SSL/TLS 加密連接

<?php
// HTTPS / SMTPS / IMAPS 等
$fp = fsockopen("ssl://example.com", 443, $errno, $errstr, 10);
// 或
$fp = fsockopen("tls://smtp.gmail.com", 587, $errno, $errstr, 10);
// 驗證證書需配合 stream_context_set_option
?>

常見錯誤與排查

錯誤原因解決
Connection refused目標端口未監聽/防火牆阻擋檢查服務狀態、安全組、iptables
Connection timed out網絡不通/防火牆丟包/超時太短增加 timeout、檢查路由、telnet 測試
SSL certificate verify failed證書不信任/過期/域名不匹配配置 stream context verify_peer 或開發環境關閉驗證
fgets 卡死阻塞模式下對端不關閉連接/無數據設置超時、非阻塞+stream_select、或用 HTTP/1.0 Connection: close

現代替代:Guzzle / Symfony HttpClient

生產環境 HTTP 客戶端**強烈建議**使用成熟庫:

composer require guzzlehttp/guzzle

<?php
$client = new GuzzleHttp\Client();
$resp = $client->get('https://api.example.com/users', ['timeout' => 10]);
$data = json_decode($resp->getBody(), true);
// 自動處理:重試、重定向、分塊、壓縮、異步、中間件、測試雙工
?>

總結

fsockopen 是 PHP 底層網絡編程的基石,理解它有助於掌握 HTTP、SMTP、FastCGI 等協議本質。但**業務代碼請直接用高級庫**。掌握:阻塞/非阻塞模式、超時控制、SSL 封裝、Unix Socket、錯誤處理,足以應對調試、探測、特殊協議等場景。

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