前言
PHP 的 socket_* 函數族是對 C 語言 Berkeley Socket API 的薄封裝,提供底層網路程式設計能力。本文整理常用函數分類說明,並附上實用範例:Echo Server (TCP)、UDP 遊戲伺服器查詢、客戶端連線。
核心函數分類速查表
1. 生命週期管理
| 函數 | 說明 |
socket_create(domain, type, protocol) | 建立 Socket 資源。domain: AF_INET(IPv4)/AF_INET6/AF_UNIX;type: SOCK_STREAM(TCP)/SOCK_DGRAM(UDP);protocol: SOL_TCP/SOL_UDP/0。 |
socket_bind(socket, address, port) | 綁定 IP 與埠 (伺服端)。address 可為 IP 或 Unix socket 路徑。 |
socket_listen(socket, backlog) | 監聽連線,backlog 為等待隊列長度。 |
socket_accept(socket) | 阻塞接受新連線,返回新 Socket 資源 (用於該客戶端通訊)。 |
socket_connect(socket, address, port) | 客戶端主動連線至伺服器。 |
socket_close(socket) | 關閉 Socket 並釋放資源。 |
2. 資料收發
| 函數 | 說明 |
socket_write(socket, buffer, length) | 發送資料。需指定長度,返回實際寫入位元組數。 |
socket_read(socket, length, type) | 讀取資料。type: PHP_BINARY_READ (二進制、含換行) / PHP_NORMAL_READ (讀到 \n/\r)。 |
socket_recv(socket, buf, len, flags) | 進階接收,flags 可用 MSG_PEEK (窺視不移除)、MSG_WAITALL (等滿 len)。 |
socket_send(socket, buf, len, flags) | 進階發送,flags 可用 MSG_DONTROUTE、MSG_OOB (帶外資料)。 |
3. 狀態與選項
| 函數 | 說明 |
socket_get_option / socket_set_option | 獲取/設定 Socket 選項 (超時、緩衝區大小、非阻塞、KeepAlive 等)。 |
socket_set_nonblock(socket) | 設為非阻塞模式,配合 socket_select 實現多路復用。 |
socket_select(read, write, except, tv_sec, tv_usec) | 多路復用核心,監控多個 Socket 可讀/可寫/例外事件。 |
socket_last_error(socket) | 取得最後錯誤代碼。 |
socket_strerror(errno) | 將錯誤代碼轉為可讀字串。 |
socket_clear_error(socket) | 清除錯誤狀態。 |
實戰範例一:TCP Echo Server (CLI 守護進程)
<?php
set_time_limit(0);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 12345);
socket_listen($socket, 5);
echo "Echo Server started on port 12345\n";
while (true) {
$client = socket_accept($socket);
socket_write($client, "Connected. Type 'quit' to exit.\n");
while (true) {
$data = socket_read($client, 1024, PHP_NORMAL_READ);
if ($data === false) break;
$data = trim($data);
if ($data === 'quit') break;
socket_write($client, "ECHO: $data\n");
}
socket_close($client);
}
socket_close($socket);
?>
實戰範例二:UDP 遊戲伺服器查詢 (以 Return to Castle Wolfenstein 為例)
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec'=>5, 'usec'=>0]); // 5 秒接收超時
$host = 'wolfmaster.idsoftware.com';
$port = 27950;
socket_connect($socket, $host, $port); // UDP "連線" 只是設定預設目標
// 發送查詢封包 (協議特定)
socket_write($socket, "\xFF\xFF\xFF\xFFgetservers\x00");
$servers = [];
while (($data = @socket_read($socket, 4096)) !== false) {
// 解析回應封包 (略)
// 每 7 位元組一筆: 4 bytes IP + 2 bytes port
for ($i = 22; $i + 5 < strlen($data); $i += 7) {
$ip = implode('.', array_map('ord', str_split(substr($data, $i+1, 4))));
$port = (ord($data[$i+5]) << 8) + ord($data[$i+6]);
$servers[] = compact('ip', 'port');
}
}
socket_close($socket);
echo "" . count($servers) . " Servers found
";
// 後續可對每台發送 getstatus 取得玩家、地圖資訊
?>
實戰範例三:同步 HTTP 客戶端 (簡易版)
<?php
function http_get($url) {
$parsed = parse_url($url);
$host = $parsed['host'];
$path = $parsed['path'] ?? '/';
$port = $parsed['port'] ?? 80;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, $host, $port);
$req = "GET $path HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n";
socket_write($sock, $req);
$resp = '';
while ($buf = socket_read($sock, 4096)) $resp .= $buf;
socket_close($sock);
return $resp;
}
echo http_get('https://example.com/');
?>
進階主題:非阻塞多路復用 (Reactor 模式)
$sockets = [$listen_socket];
while (true) {
$read = $sockets;
$write = $except = null;
if (socket_select($read, $write, $except, 1) > 0) {
foreach ($read as $sock) {
if ($sock === $listen_socket) {
$client = socket_accept($sock);
socket_set_nonblock($client);
$sockets[] = $client;
} else {
$data = socket_read($sock, 4096);
if ($data === '' || $data === false) {
socket_close($sock);
$sockets = array_diff($sockets, [$sock]);
} else {
// 處理 $data,準備回應寫入 $write 陣列...
}
}
}
}
}
常見坑與最佳實踐
- 阻塞預設:所有
socket_* 預設阻塞,長連線必須設超時 (SO_RCVTIMEO) 或改非阻塞。
- 資料邊界:TCP 是流協議,
socket_read 可能回傳不完整封包,應用層需自行定義協議 (長度前綴、分隔符)。
- 錯誤處理:每次呼叫後檢查
socket_last_error(),區分 EAGAIN/EWOULDBLOCK (非阻塞無資料) 與真實錯誤。
- 資源洩漏:異常分支務必
socket_close,長期運行程式建議定期 gc_collect_cycles()。
- IPv6 相容:使用
AF_INET6 或 stream_socket_server 雙棧綁定。
結語
掌握 PHP Socket API 能讓你突破 HTTP 框架限制,開發即時通訊、遊戲伺服器、物聯網網關、自定義協議代理等系統級應用。建議對照 官方手冊 深入研讀各函數參數細節。