首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP Socket 程式設計完整教學:從基礎概念到 Client/Server 實作

     編輯:PHP 相關知識

前言

Socket 是網路通訊的基礎抽象,PHP 透過 `ext-sockets` 擴充提供 Berkeley Socket API 的大部分功能。雖然現代開發多使用 HTTP API、WebSocket 或 gRPC,但理解 Socket 底層運作對排查網路問題、開發自訂協議、高效能長連線服務仍具關鍵價值。

一、核心概念三要素

建立 Socket 需指定三個參數:

類別常用常數說明
通訊域AF_INET (IPv4), AF_INET6 (IPv6), AF_UNIX (本機)決定位址格式與傳輸範圍
Socket 型態SOCK_STREAM (TCP, 可靠串流), SOCK_DGRAM (UDP, 無連線)決定傳輸語意
協定SOL_TCP, SOL_UDP, SOL_ICMP具體協定編號,通常由型態推導

二、基礎 Server 建立流程

<?php
// 1. 建立 Socket 資源
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) die("socket_create 失敗: " . socket_strerror(socket_last_error()));

// 2. 綁定 IP 與 Port
if (!socket_bind($socket, '0.0.0.0', 1337)) {
    die("socket_bind 失敗: " . socket_strerror(socket_last_error($socket)));
}

// 3. 開始監聽(backlog 512 為常見預設)
if (!socket_listen($socket, 512)) {
    die("socket_listen 失敗: " . socket_strerror(socket_last_error($socket)));
}

echo "Server 啟動,監聽 0.0.0.0:1337\n";

// 4. 事件迴圈(單執行緒阻塞示範)
while (true) {
    // 接受連線(阻塞直到有客戶端連入)
    $client = socket_accept($socket);
    if ($client === false) {
        echo "socket_accept 失敗: " . socket_strerror(socket_last_error($socket)) . "\n";
        continue;
    }
    $peer = socket_getpeername($client, $addr, $port);
    echo "新連線來自 $addr:$port\n";

    // 讀取資料(阻塞直到有資料或連線關閉)
    $data = socket_read($client, 4096, PHP_NORMAL_READ);
    if ($data === false) {
        echo "讀取錯誤: " . socket_strerror(socket_last_error($client)) . "\n";
    } else {
        echo "收到: $data\n";
        // 回應
        $resp = "Server 收到: $data\n";
        socket_write($client, $resp, strlen($resp));
    }

    socket_close($client);
}

// 永不執行到此,實際專案需訊號處理優雅關閉
socket_close($socket);

三、Client 連線範例

<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) die("建立失敗");

$result = socket_connect($socket, '127.0.0.1', 1337);
if ($result === false) die("連線失敗: " . socket_strerror(socket_last_error($socket)));

$msg = "Hello from PHP Client!\n";
socket_write($socket, $msg, strlen($msg));

$reply = socket_read($socket, 4096, PHP_NORMAL_READ);
echo "Server 回應: $reply";

socket_close($socket);

四、關鍵函式速查表

函式用途
socket_create建立 socket 資源
socket_bind綁定本地 IP:Port
socket_listen轉為被動監聽狀態
socket_accept接受連線,返回新 socket
socket_connect主動發起連線
socket_read讀取資料(支援長度/換行模式)
socket_write寫入資料(可能需迴圈確保全寫入)
socket_close關閉 socket 釋放資源
socket_selectI/O 多工(非阻塞/並發核心)
socket_set_nonblock設定非阻塞模式
socket_getpeername取得對端 IP:Port
socket_last_error / socket_strerror錯誤處理

五、進階主題:並發處理

單執行緒阻塞模型一次只能服務一個客戶端。生產環境常見方案:

  1. 多進程:`pcntl_fork()` 產生子進程處理各連線(PHP 傳統模式)。
  2. I/O 多工:`socket_select()` 監控多個 socket 可讀/可寫狀態,單進程高並發(如 Workerman、Swoole 底層原理)。
  3. 非阻塞 + 事件迴圈:`socket_set_nonblock()` 搭配 `libevent`/`uv`/`Swoole` 事件驅動。
  4. 預先派生 Worker 池:主進程 `accept` 後將 socket 傳遞給 Worker 進程(需 `SCM_RIGHTS` 傳遞 fd,PHP 需擴充支援)。

六、常見坑點與除錯技巧

  • 擴充未啟用:`php.ini` 需 `extension=sockets`(Windows: `php_sockets.dll`),CLI 執行 `php -m | grep sockets` 確認。
  • 權限問題:Port < 1024 需 root 權限,建議使用 >= 1024 或 `setcap` 授權。
  • 緩衝區處理:`socket_write` 不保證一次寫入全部,需檢查返回值迴圈寫入;`socket_read` 需自行處理訊息邊界(定長、分隔符、長度前綴)。
  • TIME_WAIT 佔用:快速重啟 Server 遇 `Address already in use`,設定 `SO_REUSEADDR`:
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
  • 編碼一致性:網路傳輸為位元組流,雙端需約定編碼(UTF-8)、封包格式(JSON/Protobuf/自訂二進制)。

七、實戰建議

  • 教學/原型階段可直接用 PHP Socket API;正式專案強烈建議使用 **Swoole**、**Workerman**、**Amp** 等高階框架,已封裝並發、協程、SSL、協議解析等複雜細節。
  • 若需與其他語言互通,務必定義明確的通訊協議文檔(封包結構、心跳、重連策略)。
  • 善用 `strace`、`tcpdump`/`Wireshark` 抓封包分析疑難雜症。

結語

Socket 是網路程式設計的基石。掌握 `socket_create/bind/listen/accept/read/write/close` 核心流程、理解阻塞與非阻塞模型、熟悉 `socket_select` 多工機制,即能在需要時突破 HTTP 框架限制,構建高效能、自訂協議的網路服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved