| 體系 | 函數代表 | 能力 | 適用場景 |
|---|---|---|---|
| 內核流封裝 | fsockopen, pfsockopen | 僅客端主動連接、阻塞模式、返回資源可用文件函數操作 | 簡單 HTTP 請求、SMTP 發信、WHOIS 查詢 |
| Sockets 擴展 | socket_create, socket_bind, socket_listen... | 全功能:服務端監聽、非阻塞、多路複用、UDP/Unix Domain、SSL | 高性能服務器、自定義協議、長連接、IM、遊戲 |
// 創建 Socket $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // IPv4 TCP $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP); // IPv6 $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); // Unix Domain // 綁定地址端口 socket_bind($socket, '0.0.0.0', 9501); // 監聽 socket_listen($socket, 128); // backlog 128 // 非阻塞模式 socket_set_nonblock($socket); // 或 socket_set_block($socket) 阻塞 // 接受連接 $client = socket_accept($server); // 阻塞直到有連接 // 讀寫 $data = socket_read($client, 8192); // PHP_NORMAL_READ 遇 \n/\r 停止 socket_write($client, $response, strlen($response)); // 多路複用 (select) $read = [$server, $client1, $client2]; $write = $except = null; socket_select($read, $write, $except, 0, 500000); // 0.5s 超時 // 關閉 socket_close($client); socket_close($server);
客戶端 (client.php)
運行測試
# 終端 1 啟動服務器 php server.php # 終端 2 執行客戶端 php client.php進階:基於 select 的多客戶端並發服務器
Stream Socket 封裝(更高層)
// 客戶端 HTTP POST 模擬 function post_contents($data = []) { $post = http_build_query($data); $header = "POST /api HTTP/1.1\r\n"; $header .= "Host: api.example.com\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . strlen($post) . "\r\n"; $header .= "Connection: close\r\n\r\n"; $fp = stream_socket_client("tcp://api.example.com:80", $errno, $errstr, 10); if (!$fp) return "ERROR: $errstr ($errno)"; fwrite($fp, $header . $post); $response = ''; while (!feof($fp)) { $response .= fgets($fp, 1024); } fclose($fp); return $response; }feof 死循環陷阱與正確讀取模式
// ❌ 錯誤:feof 在讀取最後一行後仍為 false,導致多讀一次空行進入死循環 while (!feof($fp)) { $line = fgets($fp); // 處理 $line } // ✅ 正確:fgets 返回 false 直接跳出 while (($line = fgets($fp, 1024)) !== false) { // 處理 $line } // ✅ 或:stream_get_contents 一次性讀取(適用小響應) $response = stream_get_contents($fp);內核源碼視角:socket_create 封裝
PHP Sockets 擴展在
ext/sockets/sockets.c中封裝系統調用:PHP_FUNCTION(socket_create) { long domain, type, protocol; php_socket *php_sock = emalloc(sizeof(php_sock)); zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &domain, &type, &protocol); php_sock->bsd_socket = socket(domain, type, protocol); // 系統調用 ZEND_REGISTER_RESOURCE(return_value, php_sock, le_socket); }本質是對 C 語言
socket()、bind()、listen()等的資源管理封裝,自動處理文件描述符生命週期。生產環境建議
SIGPIPE 忽略、心跳檢測、緩衝區滿背壓、優雅關閉、日誌監控。