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

PHP Socket 發送 HTTP 請求完全指南:從底層 Socket 到 Stream Context

     編輯:PHP 相關知識

PHP Socket 發送 HTTP 請求完全指南:從底層 Socket 到 Stream Context

在無 cURL 擴展、需要精細控制 TCP 連接參數、或實現特殊協議交互場景下,掌握 PHP 原生 Socket 發送 HTTP 請求至關重要。本文涵蓋四種遞進方案:socket_* 底層 API、fsockopen 封裝、stream_socket_client 現代 API、stream_context 高級配置,並對比優缺點與適用場景。

方案一:底層 socket_* API (最接近系統調用)

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) die("socket_create failed: " . socket_strerror(socket_last_error()));

// 可選:設置發送/接收超時
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 10, 'usec' => 0]);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 10, 'usec' => 0]);

$connected = socket_connect($socket, 'www.example.com', 80);
if (!$connected) die("connect failed: " . socket_strerror(socket_last_error($socket)));

// 構造 HTTP/1.0 請求 (無需 Host 頭,但建議加上)
$request = "GET / HTTP/1.0\r\n"
         . "Host: www.example.com\r\n"
         . "User-Agent: PHP-Socket/1.0\r\n"
         . "Connection: Close\r\n\r\n";

socket_write($socket, $request, strlen($request));

$response = '';
while ($buf = socket_read($socket, 4096)) {
    $response .= $buf;
}
socket_close($socket);

echo $response;

方案二:fsockopen 經典封裝 (PHP 4 時代遺產,仍廣泛使用)

$fp = fsockopen('www.example.com', 80, $errno, $errstr, 10); // 10秒連接超時
if (!$fp) die("$errstr ($errno)");

$request = "GET / HTTP/1.1\r\n"
         . "Host: www.example.com\r\n"
         . "Connection: Close\r\n\r\n";

fwrite($fp, $request);

$response = '';
while (!feof($fp)) {
    $response .= fgets($fp, 4096);
}
fclose($fp);

echo $response;

方案三:stream_socket_client 現代流式 API (推薦)

$fp = stream_socket_client('tcp://www.example.com:80', $errno, $errstr, 10,
    STREAM_CLIENT_CONNECT, stream_context_create());
if (!$fp) die("$errstr ($errno)");

// 設置讀寫超時 (流級別)
stream_set_timeout($fp, 10);

$request = "GET / HTTP/1.1\r\n"
         . "Host: www.example.com\r\n"
         . "Connection: Close\r\n\r\n";

fwrite($fp, $request);

$response = stream_get_contents($fp); // 一次性讀取所有
fclose($fp);

echo $response;

方案四:stream_context_create + file_get_contents (最高級抽象,支援 HTTPS/代理/POST/Header 完全配置)

// GET 請求帶自定義 Header
$headers = [
    "Host: www.example.com",
    "User-Agent: PHP-Stream/1.0",
    "Accept: application/json",
    "Accept-Language: zh-CN,zh;q=0.9",
    "Cookie: session=abc123; token=xyz",
    "Connection: close"
];

$context = stream_context_create([
    'http' => [
        'method'  => 'GET',
        'header'  => implode("\r\n", $headers),
        'timeout' => 10,           // 讀取超時
        'protocol_version' => '1.1',
        'follow_location' => 1,    // 自動跟隨重定向
        'max_redirects' => 3,
        'ignore_errors' => false,  // 非 2xx 拋出警告但仍返回內容
    ],
    'ssl' => [                     // HTTPS 專用
        'verify_peer' => false,    // 生產環境應為 true 並配置 cafile
        'verify_peer_name' => false,
    ]
]);

$response = file_get_contents('https://api.example.com/users', false, $context);
if ($response === false) die('Request failed');

// 獲取響應頭 (需 PHP 5.4+)
$meta = stream_get_meta_data($fp); // 但 file_get_contents 不返回 $fp
// 替代方案:使用 fopen + stream_get_contents
$fp = fopen('https://api.example.com/users', 'rb', false, $context);
$response = stream_get_contents($fp);
$meta = stream_get_meta_data($fp);
$http_headers = $meta['wrapper_data'] ?? [];
fclose($fp);

POST 請求完整示例

$postData = http_build_query([
    'action' => 'save',
    'id'     => 387171,
    'data'   => json_encode(['name' => 'Test', 'value' => 123])
]);

$headers = [
    "Host: api.example.com",
    "Content-Type: application/x-www-form-urlencoded",
    "Content-Length: " . strlen($postData),
    "Connection: close"
];

$context = stream_context_create([
    'http' => [
        'method'  => 'POST',
        'header'  => implode("\r\n", $headers),
        'content' => $postData,
        'timeout' => 15,
    ]
]);

$response = file_get_contents('https://api.example.com/endpoint', false, $context);

關鍵差異對比表

特性socket_*fsockopenstream_socket_clientstream_context
抽象層級最低 (系統調用)低中高 (聲明式)
HTTPS 支援需手動 SSL/TLS需手動 SSL/TLS支援 ssl:// 協議原生支援 https://
超時控制socket_set_option參數/stream_set_timeoutstream_set_timeoutcontext timeout 參數
代理支援手動實現手動實現手動實現context proxy 參數
錯誤處理socket_last_errorerrno/errstrerrno/errstr標準錯誤/異常
性能最高 (無封裝)高高略低 (解析開銷)
推薦場景自定義協議/極致性能舊項目維護/簡單 TCP現代 TCP 客戶端HTTP/HTTPS 標準請求

HTTP/1.1 vs HTTP/1.0 關鍵點

  • Host 頭:HTTP/1.1 強制要求 Host 頭,否則服務器返回 400 Bad Request。
  • 持久連接:HTTP/1.1 預設 Connection: keep-alive,需顯式發送 Connection: close 或處理 Content-Length/Transfer-Encoding: chunked 讀取完整響應。
  • Chunked 編碼:stream_context 自動處理,底層 Socket 需手動解析。

生產環境建議

  1. 優先使用 cURL:功能最全、性能最好、支援 HTTP/2、證書驗證、多路複用、異步多請求。
  2. 次選 stream_context:無 cURL 時的標準選擇,代碼簡潔、配置聲明式。
  3. 避免手動拼接 HTTP 協議:易出錯 (換行符、編碼、分塊傳輸),除非極簡場景或教學演示。
  4. 超時策略:連接超時 (3-5s) + 讀取超時 (10-30s) 分離設置,防止掛起進程。
  5. 錯誤重試:冪等請求 (GET/HEAD) 實現指數退避重試 (3次),記錄失敗日誌供告警。

理解底層 Socket 到高層 Stream Context 的演進脈絡,能讓你在受限環境、特殊協議、性能調優等場景做出最佳技術選型。

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