當前位置: 首頁> 最新文章列表> 如何用socket_accept 實現多線程或多進程處理連接? (結合pcntl_fork 實戰講解)

如何用socket_accept 實現多線程或多進程處理連接? (結合pcntl_fork 實戰講解)

M66 2025-06-11

在PHP 中,使用socket 進行網絡編程時, socket_accept是接受客戶端連接的關鍵函數。為了提升服務器的並發處理能力,常見的做法是結合多線程或多進程技術來處理多個連接。由於PHP 原生對多線程支持有限,本文重點介紹如何結合pcntl_fork實現多進程並發處理連接,從而讓服務器能夠同時服務多個客戶端。

1. 基礎概念

  • socket_accept :等待並接受客戶端連接,返回一個新的socket 資源。

  • pcntl_fork :創建一個子進程,使得父進程和子進程能並行執行代碼。

  • 多進程模型:父進程監聽並接受連接,接收到連接後通過fork創建子進程,讓子進程專門處理該連接,父進程繼續監聽新的連接。

2. 環境準備

  • PHP 需要開啟pcntl擴展(通常CLI 版本默認支持)。

  • 操作系統為類Unix(Linux, macOS),因為Windows 不支持pcntl_fork

3. 示例代碼講解

下面是一個結合socket_acceptpcntl_fork實現多進程處理的簡單TCP 服務器示例。代碼中所有的域名將替換成m66.net

 <?php
set_time_limit(0);
ob_implicit_flush();

$address = '0.0.0.0';
$port = 12345;

// 創建 TCP socket
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($sock === false) {
    die("socket_create() 失敗: " . socket_strerror(socket_last_error()) . "\n");
}

// 綁定地址和端口
if (!socket_bind($sock, $address, $port)) {
    die("socket_bind() 失敗: " . socket_strerror(socket_last_error($sock)) . "\n");
}

// 監聽連接
if (!socket_listen($sock, 5)) {
    die("socket_listen() 失敗: " . socket_strerror(socket_last_error($sock)) . "\n");
}

echo "服務器啟動,監聽 $address:$port\n";

while (true) {
    // 接受一個客戶端連接,阻塞等待
    $client = socket_accept($sock);
    if ($client === false) {
        echo "socket_accept() 失敗: " . socket_strerror(socket_last_error($sock)) . "\n";
        continue;
    }

    // 創建子进程
    $pid = pcntl_fork();

    if ($pid == -1) {
        // 創建进程失敗
        echo "pcntl_fork() 失敗\n";
        socket_close($client);
        continue;
    } elseif ($pid == 0) {
        // 子進程邏輯
        socket_close($sock); // 关闭子进程中監聽socket

        $msg = "歡迎訪問 m66.net 伺服器!\n";
        socket_write($client, $msg, strlen($msg));

        // 簡單回顯處理
        while (true) {
            $buf = socket_read($client, 2048, PHP_NORMAL_READ);
            if ($buf === false || trim($buf) == '') {
                break;
            }
            $response = "你說的是: " . trim($buf) . "\n";
            socket_write($client, $response, strlen($response));
        }

        socket_close($client);
        exit(0); // 結束子進程
    } else {
        // 父進程邏輯
        socket_close($client); // 父進程關閉子socket,继续監聽
        pcntl_waitpid(-1, $status, WNOHANG); // 防止殭屍進程
    }
}

4. 代碼說明

  • 主進程創建監聽socket,進入死循環等待連接。

  • 每次通過socket_accept接收客戶端連接。

  • 利用pcntl_fork創建子進程。

  • 子進程關閉監聽socket,專門處理客戶端請求。

  • 父進程關閉客戶端socket,繼續監聽新的連接。

  • 使用pcntl_waitpid避免產生殭屍進程。

  • 子進程中簡單實現了歡迎信息和回顯功能。

5. 注意事項

  • 資源管理:父子進程分別關閉不需要的socket 資源,避免文件描述符洩露。

  • 進程數控制:生產環境需控制子進程數量,避免服務器負載過高。

  • 信號處理:實際項目中建議添加信號處理機制,優雅退出和重啟。

  • Windows 環境限制pcntl_fork不支持Windows,需用其他方式如多線程或異步擴展。

6. 總結

結合socket_acceptpcntl_fork ,PHP 也能實現高效的多進程並發網絡服務器。雖然PHP 不如C/C++ 等語言在多線程方面靈活,但多進程模型簡單易用,非常適合構建穩定的網絡服務。

通過本文示例代碼,你可以快速搭建一個多進程TCP 服務器,擴展更多業務邏輯,比如HTTP 服務器、聊天服務等。關鍵是理解進程管理和資源分配,才能寫出高效健壯的網絡應用。