在無 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);
$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_* | fsockopen | stream_socket_client | stream_context |
|---|---|---|---|---|
| 抽象層級 | 最低 (系統調用) | 低 | 中 | 高 (聲明式) |
| HTTPS 支援 | 需手動 SSL/TLS | 需手動 SSL/TLS | 支援 ssl:// 協議 | 原生支援 https:// |
| 超時控制 | socket_set_option | 參數/stream_set_timeout | stream_set_timeout | context timeout 參數 |
| 代理支援 | 手動實現 | 手動實現 | 手動實現 | context proxy 參數 |
| 錯誤處理 | socket_last_error | errno/errstr | errno/errstr | 標準錯誤/異常 |
| 性能 | 最高 (無封裝) | 高 | 高 | 略低 (解析開銷) |
| 推薦場景 | 自定義協議/極致性能 | 舊項目維護/簡單 TCP | 現代 TCP 客戶端 | HTTP/HTTPS 標準請求 |
Connection: keep-alive,需顯式發送 Connection: close 或處理 Content-Length/Transfer-Encoding: chunked 讀取完整響應。stream_context 自動處理,底層 Socket 需手動解析。理解底層 Socket 到高層 Stream Context 的演進脈絡,能讓你在受限環境、特殊協議、性能調優等場景做出最佳技術選型。