Socket 是應用層與 TCP/IP 協議棧交互的門面模式封裝。本文系統梳理 Socket 核心概念、通信流程、PHP 內建函數速查,並提供可直接用於生產的 ServerSocket / ClientSocket 封裝類。
| 協議 | 全稱 | 特點 |
|---|---|---|
| TCP | Transmission Control Protocol | 面向連接、可靠、有序、流式、開銷大 |
| UDP | User Datagram Protocol | 無連接、不可靠、無序、數據報、開銷小、適合廣播/視頻 |
Socket 位於應用層與傳輸層之間,屏蔽了協議細節,提供統一介面:create → bind → listen → accept → read/write → close。
// 生命週期
socket_create() // 建立 socket 資源
socket_bind() // 綁定 IP:PORT
socket_listen() // 監聽連接隊列
socket_accept() // 接受連接,返回新 socket
socket_connect() // 客戶端發起連接
// 數據收發
socket_read() // 讀取指定長度
socket_write() // 寫入緩衝區
socket_recv() // 從已連接 socket 讀取
socket_send() // 發送到已連接 socket
// 工具
socket_close() // 關閉資源
socket_last_error() // 獲取錯誤碼
socket_strerror() // 錯誤碼轉文本
socket_set_nonblock() // 非阻塞模式
socket_select() // I/O 多路複用(關鍵!)
...
<?php
set_time_limit(0);
$host = '127.0.0.1'; $port = 1935;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, $host, $port);
socket_listen($sock, 5);
echo "Server started on $host:$port\n";
while (true) {
$client = socket_accept($sock);
$input = socket_read($client, 8192);
echo "Received: $input\n";
$reply = "Server echo: $input";
socket_write($client, $reply);
socket_close($client);
}
socket_close($sock);
<?php
$host = '127.0.0.1'; $port = 1935;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, $host, $port);
$msg = "Hello from Client\n";
socket_write($sock, $msg);
$resp = socket_read($sock, 8192);
echo "Server reply: $resp\n";
socket_close($sock);
php server.php),不可用 CGI/瀏覽器訪問。netstat -ano | findstr 1935 (Win) / ss -ltnp | grep 1935 (Linux)。<?php
class ServerSocket {
private $host, $port, $master, $clients = [];
public function __construct(string $host, int $port) {
if (!extension_loaded('sockets')) die("請啟用 sockets 擴展");
$this->host = $host; $this->port = $port;
$this->initMaster();
}
private function initMaster() {
$this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($this->master, $this->host, $this->port);
socket_listen($this->master, SOMAXCONN);
$this->clients[] = $this->master;
}
public function run() {
while (true) {
$read = $this->clients;
$write = $except = null;
if (socket_select($read, $write, $except, null) < 1) continue;
foreach ($read as $sock) {
if ($sock === $this->master) {
// 新連接
$client = socket_accept($this->master);
$this->clients[] = $client;
echo "New client connected\n";
} else {
// 數據到達
$data = @socket_read($sock, 8192, PHP_BINARY_READ);
if ($data === false || $data === '') {
// 斷開
$idx = array_search($sock, $this->clients, true);
if ($idx !== false) unset($this->clients[$idx]);
socket_close($sock);
continue;
}
// 業務處理(示例:回射)
$reply = $this->handle($data);
socket_write($sock, $reply);
}
}
}
}
protected function handle(string $data): string {
return "Echo: $data";
}
public function __destruct() {
foreach ($this->clients as $sock) @socket_close($sock);
}
}
// 使用
$server = new ServerSocket('0.0.0.0', 9501);
$server->run();
<?php
class ClientSocket {
private $host, $port, $sock, $timeout = 5.0;
public function __construct(string $host, int $port, float $timeout = 5.0) {
$this->host = $host; $this->port = $port; $this->timeout = $timeout;
$this->connect();
}
private function connect() {
$this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->sock, SOL_SOCKET, SO_RCVTIMEO, ['sec' => (int)$this->timeout, 'usec' => 0]);
socket_set_option($this->sock, SOL_SOCKET, SO_SNDTIMEO, ['sec' => (int)$this->timeout, 'usec' => 0]);
if (!@socket_connect($this->sock, $this->host, $this->port)) {
throw new RuntimeException("Connect failed: " . socket_strerror(socket_last_error()));
}
}
public function send(string $data): string {
socket_write($this->sock, $data);
$buffer = '';
while ($chunk = @socket_read($this->sock, 4096)) {
$buffer .= $chunk;
if (strlen($chunk) < 4096) break; // 簡易判斷結束
}
return $buffer;
}
public function close() {
if ($this->sock) { socket_close($this->sock); $this->sock = null; }
}
public function __destruct() { $this->close(); }
}
// 使用
$client = new ClientSocket('127.0.0.1', 9501);
echo $client->send("Hello\n");
$client->close();
textalk/websocket 或 Swoole 內建 WebSocket 伺服器。