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) |
<?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 響應 (頭 + 體)
?>
<?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}');
?>
<?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、佇列
?>
<?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";
}
?>
<?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);
}
?>
<?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);
}
?>
<?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 |
生產環境 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、錯誤處理,足以應對調試、探測、特殊協議等場景。