Socket 是網絡通信的基石,PHP 通過 sockets 擴展提供了完整的底層網絡編程能力。無論是構建高性能 TCP 服務、即時聊天系統,還是對接 NNTP 新聞組、自定義協議服務,PHP 都能勝任。本文系統梳理 Socket 基礎概念、PHP API 調用、TCP 服務器實現、聊天室架構設計及 NNTP 客戶端開發。
Socket 是雙向通信鏈路的端點。客戶端與服務器通過 Socket 交換位元組流,雙方需遵循共同協議(如 HTTP、自定義協議)。關鍵要素:
| 階段 | 函數 | 說明 |
|---|---|---|
| 創建 | socket_create() | 建立 Socket 資源 |
| 綁定 | socket_bind() | 綁定 IP 與端口 |
| 監聽 | socket_listen() | 進入監聽狀態,設定佇列長度 |
| 接受 | socket_accept() | 阻塞等待客戶端連接,返回新 Socket |
| 讀寫 | socket_read()/socket_write() | 數據收發 |
| 多路復用 | socket_select() | 同時監控多個 Socket 可讀/可寫/異常 |
| 關閉 | socket_close() | 釋放資源 |
<?php
// server.php - CLI 模式運行
set_time_limit(0); // 取消執行時間限制
$host = '0.0.0.0';
$port = 9501;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, $host, $port);
socket_listen($socket, 128);
echo "Server started on {$host}:{$port}\n";
$clients = [$socket];
while (true) {
$read = $clients;
$write = $except = null;
// 多路復用:監控所有客戶端 Socket
if (socket_select($read, $write, $except, null) < 1) continue;
foreach ($read as $sock) {
if ($sock === $socket) {
// 新連接
$client = socket_accept($socket);
$clients[] = $client;
$ip = $port = null;
socket_getpeername($client, $ip, $port);
echo "New client: {$ip}:{$port}\n";
socket_write($client, "Welcome to PHP TCP Server!\n");
} else {
// 數據讀取
$data = @socket_read($sock, 4096, PHP_NORMAL_READ);
if ($data === false || $data === "") {
// 客戶端斷開
$idx = array_search($sock, $clients);
unset($clients[$idx]);
socket_close($sock);
echo "Client disconnected\n";
} else {
// 回顯
socket_write($sock, "You said: {$data}\n");
}
}
}
}
運行方式:php server.php(需 CLI 模式,已編譯 --enable-sockets)
測試:telnet 127.0.0.1 9501
聊天室本質是「一對多」廣播模型。核心挑戰在於單進程同時處理多客戶端收發,解決方案是 I/O 多路復用(select/poll/epoll)。PHP 的 socket_select() 封裝了底層 select 系統調用,適用於中小規模並發(百~千級連接)。
// 在讀取到數據後,廣播給所有其他客戶端
foreach ($clients as $client) {
if ($client !== $socket && $client !== $sender) {
socket_write($client, "User {$sender_id}: {$data}\n");
}
}
生產環境建議:高並發場景請考慮 Swoole、Workerman 等基於 epoll/kqueue 的高性能框架,或使用 Node.js/Go 等原生支援高並發的語言。
PHP 內建 fsockopen() 可快速建立 TCP 連接,配合 NNTP 協議(RFC 977)實現新聞組瀏覽:
<?php
$server = 'news.php.net';
$port = 119;
$timeout = 10;
$conn = fsockopen($server, $port, $errno, $errstr, $timeout);
if (!$conn) die("Connection failed: $errstr ($errno)");
echo fgets($conn, 1024); // 讀取歡迎信息
// 選擇新聞組
fputs($conn, "GROUP alt.php\n");
$resp = fgets($conn, 1024);
list($code, $count, $first, $last) = explode(' ', $resp);
echo "Articles: $first - $last\n";
// 讀取最新 10 條
for ($i = $last; $i > $last - 10; $i--) {
fputs($conn, "ARTICLE $i\n");
echo fgets($conn, 1024); // 文章頭
// 讀取正文...
}
fclose($conn);
socket_last_error() 獲取錯誤碼socket_set_nonblock() 配合 socket_select() 實現高並發socket_close(),防止文件描述符洩漏PHP Socket 編程雖不如 C/C++ 底層可控,但對於中小型網絡服務、內部工具、協議對接完全足夠。掌握多路復用與非阻塞模式,是編寫健壯網絡程序的關鍵。