fsockopen() 是 PHP 底層**網路通訊基石**,可建立 TCP/UDP/Unix Socket 連線,**不依賴 cURL 擴充套件**,適合輕量環境、教學示範、特殊協定實作。本文詳細說明語法、HTTP/1.0 請求手工組裝、GET/POST 完整範例、非阻塞模式、SSL/TLS、超時控制與錯誤處理最佳實務。
fsockopen(
string $hostname,
int $port = -1,
int &$errno = null,
string &$errstr = null,
float $timeout = ini_get('default_socket_timeout')
): resource|false
$hostname:支援 tcp://、udp://、ssl://、tls://、unix:// 封裝協定。$port:目標埠號,-1 表示 Unix Socket。$errno, $errstr:傳址回傳系統錯誤碼/訊息。$timeout:連線階段超時 (秒),**不包含傳輸階段**;傳輸超時需 stream_set_timeout()。<?php
$host = 'www.example.com';
$port = 80;
$path = '/';
$fp = @fsockopen($host, $port, $errno, $errstr, 10);
if (!$fp) { die("連線失敗: $errstr ($errno)"); }
$out = "GET $path HTTP/1.0\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: close\r\n\r\n";
fwrite($fp, $out);
$response = '';
while (!feof($fp)) { $response .= fgets($fp, 4096); }
fclose($fp);
// 簡易分離 header/body
list($header, $body) = explode("\r\n\r\n", $response, 2);
echo $body;
function httpPost(string $host, string $path, array $data, int $port = 80, float $timeout = 10): string {
$body = http_build_query($data);
$fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) throw new Exception("Connect failed: $errstr ($errno)");
$req = "POST $path HTTP/1.0\r\n";
$req .= "Host: $host\r\n";
$req .= "Content-Type: application/x-www-form-urlencoded\r\n";
$req .= "Content-Length: " . strlen($body) . "\r\n";
$req .= "Connection: close\r\n\r\n";
$req .= $body;
fwrite($fp, $req);
// 設定讀取超時 (傳輸階段)
stream_set_timeout($fp, $timeout);
$resp = '';
$info = ['timed_out' => false];
while (!feof($fp) && !$info['timed_out']) {
$resp .= fgets($fp, 4096);
$info = stream_get_meta_data($fp);
}
fclose($fp);
// 簡易分離
[$header, $body] = explode("\r\n\r\n", $resp, 2) + ['', ''];
return $body;
}
// 使用
echo httpPost('httpbin.org', '/post', ['field1'=>'value1', 'field2'=>'value2']);
$fp = fsockopen('ssl://api.github.com', 443, $errno, $errstr, 10);
// 或
$fp = fsockopen('tls://api.github.com', 443, $errno, $errstr, 10);
// 其餘請求邏輯相同
// 注意:需驗證憑證時請改用 stream_context_create() 設定 verify_peer/cafile
$fp = fsockopen('www.example.com', 80);
stream_set_blocking($fp, 0); // 非阻塞
// 使用 stream_select() 同時監聽多個 socket
$read = [$fp]; $write = []; $except = [];
if (stream_select($read, $write, $except, 5)) {
// $fp 可讀/可寫
}
| errno | 常數 | 說明 |
|---|---|---|
| 111 | ECONNREFUSED | 連線被拒絕 (服務未啟動/防火牆) |
| 110 | ETIMEDOUT | 連線逾時 |
| 113 | EHOSTUNREACH | 主機不可達 |
| 32 | EPIPE | 寫入斷管 (對端已關閉) |
| 104 | ECONNRESET | 連線被重置 |
| 場景 | 推薦 | 理由 |
|---|---|---|
| 生產環境 HTTP API 串接 | Guzzle / cURL | 完整重導向、Cookie、Keep-Alive、並發、中介軟體、測試雙工 |
| 無 cURL 擴充套件的受限環境 | fsockopen | 核心內建、零相依 |
| 自訂協定 (SMTP, POP3, WebSocket, DNS, 游戲協定) | fsockopen / stream_socket_client | 完整控制幀格式、握手流程 |
| 教學/理解 HTTP 底層 | fsockopen | 手工組裝請求/回應,學習價值高 |
| 高併發非阻塞 | Swoole / OpenSwoole / ReactPHP | 事件驅動、協程、原生異步 |
@ 抑制警告並自行處理 $errno/$errstr。fsockopen(..., $connectTimeout) + stream_set_timeout($fp, $readTimeout)。feof() 迴圈,避免 fread($fp, 8192) 卡住;大回應考慮 stream_get_contents()。http_parse_message() (pecl_http) 或自寫狀態機。verify_peer、cafile,否則遭中間人攻擊。fclose($fp),建議用 try/finally。fsockopen() 雖非現代 HTTP Client 首選,但作為**網路程式設計基石**、**無相依環境救生圈**、**自訂協定實作利器**,深入掌握其行為邊界對 PHP 開發者極具價值。