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

PHP Socket 編程完整指南:從基礎 API 到高併發服務器模型

     編輯:PHP 綜合知識

Socket 兩大 API 體系

體系函數代表能力適用場景
內核流封裝fsockopen, pfsockopen僅客端主動連接、阻塞模式、返回資源可用文件函數操作簡單 HTTP 請求、SMTP 發信、WHOIS 查詢
Sockets 擴展socket_create, socket_bind, socket_listen...全功能:服務端監聽、非阻塞、多路複用、UDP/Unix Domain、SSL高性能服務器、自定義協議、長連接、IM、遊戲

Sockets 擴展核心函數速查

// 創建 Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // IPv4 TCP
$socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP); // IPv6
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);        // Unix Domain

// 綁定地址端口
socket_bind($socket, '0.0.0.0', 9501);

// 監聽
socket_listen($socket, 128); // backlog 128

// 非阻塞模式
socket_set_nonblock($socket); // 或 socket_set_block($socket) 阻塞

// 接受連接
$client = socket_accept($server); // 阻塞直到有連接

// 讀寫
$data = socket_read($client, 8192); // PHP_NORMAL_READ 遇 \n/\r 停止
socket_write($client, $response, strlen($response));

// 多路複用 (select)
$read = [$server, $client1, $client2];
$write = $except = null;
socket_select($read, $write, $except, 0, 500000); // 0.5s 超時

// 關閉
socket_close($client);
socket_close($server);

完整 TCP 服務器/客戶端示例

服務器端 (server.php)



客戶端 (client.php)



運行測試

# 終端 1 啟動服務器
php server.php

# 終端 2 執行客戶端
php client.php

進階:基於 select 的多客戶端並發服務器



Stream Socket 封裝(更高層)

// 客戶端 HTTP POST 模擬
function post_contents($data = []) {
    $post = http_build_query($data);
    $header = "POST /api HTTP/1.1\r\n";
    $header .= "Host: api.example.com\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($post) . "\r\n";
    $header .= "Connection: close\r\n\r\n";
    
    $fp = stream_socket_client("tcp://api.example.com:80", $errno, $errstr, 10);
    if (!$fp) return "ERROR: $errstr ($errno)";
    
    fwrite($fp, $header . $post);
    $response = '';
    while (!feof($fp)) {
        $response .= fgets($fp, 1024);
    }
    fclose($fp);
    return $response;
}

feof 死循環陷阱與正確讀取模式

// ❌ 錯誤:feof 在讀取最後一行後仍為 false,導致多讀一次空行進入死循環
while (!feof($fp)) {
    $line = fgets($fp);
    // 處理 $line
}

// ✅ 正確:fgets 返回 false 直接跳出
while (($line = fgets($fp, 1024)) !== false) {
    // 處理 $line
}

// ✅ 或:stream_get_contents 一次性讀取(適用小響應)
$response = stream_get_contents($fp);

內核源碼視角:socket_create 封裝

PHP Sockets 擴展在 ext/sockets/sockets.c 中封裝系統調用:

PHP_FUNCTION(socket_create) {
    long domain, type, protocol;
    php_socket *php_sock = emalloc(sizeof(php_sock));
    zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &domain, &type, &protocol);
    php_sock->bsd_socket = socket(domain, type, protocol); // 系統調用
    ZEND_REGISTER_RESOURCE(return_value, php_sock, le_socket);
}

本質是對 C 語言 socket()、bind()、listen() 等的資源管理封裝,自動處理文件描述符生命週期。

生產環境建議

  • 高併發優先使用 Swoole / Workerman / ReactPHP,底層基於 epoll/kqueue,支援協程、數萬並發。
  • 原生 Socket 適用於:學習原理、簡單工具、無法安裝擴展環境、極低併發服務。
  • 必須處理:SIGPIPE 忽略、心跳檢測、緩衝區滿背壓、優雅關閉、日誌監控。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved