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

PHP Socket 核心原理與 HTTP 伺服端實作:從基礎到多客戶端處理

     編輯:PHP 綜合知識

前言

Socket 是網路通訊的基礎抽象。PHP 封裝 Berkeley Socket API,支援 TCP/UDP、IPv4/IPv6、Unix Domain Socket。本文系統講解 Socket 三要素(協定族、型別、協定)、核心函式流程,並逐步實作從單連線回聲伺服端到支援多客戶端的簡易 HTTP 伺服端。

一、Socket 三要素

層面常數說明
協定族 (Domain)AF_INETIPv4,最常用
AF_INET6IPv6
AF_UNIX本地進程間通訊 (IPC),效能最高
Socket 型別SOCK_STREAM串流、可靠、有序、TCP
SOCK_DGRAM資料報、不可靠、無序、UDP
SOCK_RAW原始封包、ICMP (ping/traceroute)
協定SOL_TCP / IPPROTO_TCPTCP 協定
SOL_UDP / IPPROTO_UDPUDP 協定
SOL_ICMPICMP 協定

二、伺服端核心流程

$proto = getprotobyname('tcp'); // 或 SOL_TCP
$socket = socket_create(AF_INET, SOCK_STREAM, $proto);
socket_bind($socket, '0.0.0.0', 1337); // 監聽所有介面
socket_listen($socket, SOMAXCONN);     // 佇列長度

while (true) {
    $client = socket_accept($socket);  // 阻塞等待
    // 處理 $client...
    socket_close($client);
}
socket_close($socket);

三、進階:支援多客戶端的回聲伺服端

單執行緒循環接受、處理、關閉,無法並發。改用 socket_select 多路複用:

<?php
// server_multi.php
set_time_limit(0);
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($master, '0.0.0.0', 1337);
socket_listen($master, SOMAXCONN);
socket_set_nonblock($master); // 關鍵:非阻塞

$clients = [$master => $master];
$buffers = [];

echo "多客戶端伺服端啟動於 0.0.0.0:1337" . PHP_EOL;

while (true) {
    $read = $clients;
    $write = $except = null;
    $num = @socket_select($read, $write, $except, 1); // 1秒超時

    if ($num === false) continue;

    foreach ($read as $sock) {
        if ($sock === $master) {
            // 新連線
            $client = socket_accept($master);
            if ($client !== false) {
                socket_set_nonblock($client);
                $clients[(int)$client] = $client;
                $buffers[(int)$client] = '';
                echo "新客戶端連入: " . (int)$client . PHP_EOL;
            }
        } else {
            // 資料到達
            $data = @socket_read($sock, 4096, PHP_BINARY_READ);
            if ($data === false || $data === '') {
                // 斷線
                $id = (int)$sock;
                unset($clients[$id], $buffers[$id]);
                socket_close($sock);
                echo "客戶端 $id 斷開" . PHP_EOL;
            } else {
                $id = (int)$sock;
                $buffers[$id] .= $data;
                // 簡單協定:以 \n 為訊息界限
                while (($pos = strpos($buffers[$id], "\n")) !== false) {
                    $msg = substr($buffers[$id], 0, $pos);
                    $buffers[$id] = substr($buffers[$id], $pos + 1);
                    echo "客戶端 $id: $msg" . PHP_EOL;
                    // 回聲
                    socket_write($sock, "Echo: $msg\n");
                }
            }
        }
    }
}

四、簡易 HTTP 伺服端實作

HTTP/1.0 簡單實作:解析請求行、標頭、回應靜態檔案/動態內容。

<?php
// http_server.php
require 'server_multi.php'; // 繼承上述多路複用架構

// 修改資料處理部分
function handleHttpRequest(string $request, string $docRoot): string {
    // 1. 解析請求行
    $lines = explode("\n", $request);
    $requestLine = trim($lines[0]);
    if (!preg_match('/^(GET|POST|HEAD)\s+(\S+)\s+HTTP\/(\d\.\d)$/', $requestLine, $m)) {
        return "HTTP/1.0 400 Bad Request\r\n\r\n";
    }
    [$method, $uri, $version] = [$m[1], $m[2], $m[3]];

    // 2. 解析標頭 (簡易)
    $headers = [];
    foreach (array_slice($lines, 1) as $line) {
        $line = trim($line);
        if ($line === '') break; // 標頭結束
        if (strpos($line, ':') !== false) {
            [$k, $v] = explode(':', $line, 2);
            $headers[strtolower(trim($k))] = trim($v);
        }
    }

    // 3. 路由與安全
    $path = parse_url($uri, PHP_URL_PATH);
    $path = $path === '/' ? '/index.html' : $path;
    $realPath = realpath($docRoot . $path);
    if ($realPath === false || strpos($realPath, realpath($docRoot)) !== 0) {
        return "HTTP/1.0 404 Not Found\r\n\r\n

404 Not Found

"; } // 4. 回應檔案 if (is_dir($realPath)) { $realPath = realpath($realPath . '/index.html'); if (!$realPath) return "HTTP/1.0 403 Forbidden\r\n\r\n"; } $mime = mime_content_type($realPath) ?: 'application/octet-stream'; $content = file_get_contents($realPath); $len = strlen($content); return "HTTP/1.0 200 OK\r\n" . "Content-Type: $mime\r\n" . "Content-Length: $len\r\n" . "Connection: close\r\n" . "\r\n" . $content; } // 在 server_multi.php 的資料處理處呼叫: // $response = handleHttpRequest($msg, __DIR__ . '/public'); // socket_write($sock, $response);

五、客戶端測試:Telnet / 瀏覽器 / PHP 腳本

# Telnet 測試回聲伺服端
telnet 127.0.0.1 1337

# 瀏覽器測試 HTTP 伺服端
https://127.0.0.1:1337/

# PHP 客戶端腳本
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 1337);
socket_write($socket, "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n");
while ($data = socket_read($socket, 1024)) echo $data;
socket_close($socket);

六、關鍵函式深度解析

socket_read 讀取模式

  • PHP_BINARY_READ:讀取指定長度原始資料,預設。
  • PHP_NORMAL_READ:讀取直到 `\n` 或 `\r\n`,適合行協定。

socket_select 超時策略

  • tv_sec=0, tv_usec=0:非阻塞輪詢,CPU 佔用高。
  • tv_sec=1, tv_usec=0:平衡回應速度與 CPU。
  • tv_sec=null:永久阻塞,直到有事件 (信號會中斷)。

錯誤處理模式


// 風格 1:檢查返回值
if (($socket = socket_create(...)) === false) { /* 錯誤 */ }

// 風格 2:@ 抑制警告 + socket_last_error()
$socket = @socket_create(...);
if ($socket === false) {
    $err = socket_last_error();
    echo socket_strerror($err);
}

七、從 Socket 到現代框架的演進

層級代表技術特點
原生 Socketext-sockets完全控制、學習用、維護成本高
Stream 封裝stream_socket_server/client內建、支援 SSL/TLS、資源型別統一
事件驅動庫ReactPHP, Amp, libevent非阻塞、Promise、生態豐富
協程框架Swoole, OpenSwoole, RoadRunnerC 擴展、協程、極致效能、生產就緒
應用框架Laravel Octane, Hyperf, Symfony Mercure整合業務框架、開發體驗佳

八、總結

Socket 程式設計核心在於建立通訊端點、綁定位址、監聽/連接、資料收發、優雅關閉。PHP 提供完整的 Berkeley Socket API 封裝,配合 socket_select 可實現高併發單執行緒伺服端。理解阻塞/非阻塞、讀取模式、錯誤處理、協定解析細節,是進階網路程式設計的基石。生產系統強烈建議使用 Swoole 等成熟協程框架,專注業務創新。

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