PHP 怎么TCP服务器

wen PHP项目 2

本文目录导读:

PHP 怎么TCP服务器

  1. 使用 stream_socket_server()(基础方案)
  2. 使用 pcntl 多进程方案
  3. 使用 Swoole 扩展(推荐)
  4. 完整的客户端测试代码
  5. 使用 Workerman(另一个常见方案)
  6. 性能对比
  7. 安装建议
  8. 注意事项

我来详细介绍如何在PHP中创建TCP服务器,包含多种实现方案:

使用 stream_socket_server()(基础方案)

这是PHP内置的最简单方式:

<?php
// 创建TCP服务器
$server = stream_socket_server(
    "tcp://0.0.0.0:8080",
    $errno,
    $errstr
);
if (!$server) {
    die("无法创建服务器: $errstr ($errno)");
}
echo "TCP服务器已启动,监听 0.0.0.0:8080\n";
// 设置非阻塞模式
stream_set_blocking($server, false);
// 存储所有客户端连接
$clients = [$server];
while (true) {
    $read = $clients;
    $write = null;
    $except = null;
    // 使用select监听连接
    if (stream_select($read, $write, $except, 0, 10000) > 0) {
        // 新连接
        if (in_array($server, $read)) {
            $client = stream_socket_accept($server);
            if ($client) {
                stream_set_blocking($client, false);
                $clients[] = $client;
                echo "新客户端连接: " . stream_socket_get_name($client, true) . "\n";
            }
            // 移除服务器对象,避免重复处理
            $key = array_search($server, $read);
            unset($read[$key]);
        }
        // 处理客户端数据
        foreach ($read as $client) {
            $data = fread($client, 8192);
            if ($data === false || $data === '') {
                // 客户端断开
                echo "客户端断开连接\n";
                fclose($client);
                $key = array_search($client, $clients);
                unset($clients[$key]);
            } else {
                // 处理接收到的数据
                echo "收到数据: " . trim($data) . "\n";
                // 发送响应
                fwrite($client, "服务器收到: " . $data);
                // 如果收到exit命令,关闭连接
                if (trim($data) === "exit") {
                    fclose($client);
                    $key = array_search($client, $clients);
                    unset($clients[$key]);
                }
            }
        }
    }
}

使用 pcntl 多进程方案

适合需要高并发的场景:

<?php
// 创建服务器
$server = stream_socket_server(
    "tcp://0.0.0.0:8080",
    $errno,
    $errstr
);
if (!$server) {
    die("无法创建服务器: $errstr ($errno)");
}
// fork 5个子进程
$workers = 5;
for ($i = 0; $i < $workers; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("无法创建子进程");
    } elseif ($pid) {
        // 父进程继续
        continue;
    } else {
        // 子进程处理连接
        while (true) {
            $client = @stream_socket_accept($server);
            if ($client) {
                // 处理客户端请求
                $pid = posix_getpid();
                echo "进程 $pid 处理新连接\n";
                // 读取数据
                $request = fread($client, 8192);
                echo "收到: " . $request . "\n";
                // 发送响应
                $response = "处理完成 (进程 $pid): " . $request;
                fwrite($client, $response);
                fclose($client);
            }
            usleep(100);
        }
    }
}
// 父进程等待子进程
while (true) {
    $status = 0;
    $pid = pcntl_wait($status);
    echo "子进程 $pid 退出\n";
}

使用 Swoole 扩展(推荐)

Swoole是PHP最完善的TCP服务器解决方案:

<?php
// 创建Swoole TCP服务器
$server = new Swoole\Server('0.0.0.0', 8080);
// 设置运行参数
$server->set([
    'worker_num' => 4,           // worker进程数
    'backlog' => 128,            // 监听队列长度
    'max_connection' => 10000,   // 最大连接数
    'daemonize' => false,        // 是否守护进程化
]);
// 监听连接建立事件
$server->on('Connect', function ($server, $fd) {
    echo "客户端连接: fd=$fd\n";
});
// 监听数据接收事件
$server->on('Receive', function ($server, $fd, $reactor_id, $data) {
    echo "收到数据 fd=$fd: " . $data . "\n";
    // 发送响应
    $server->send($fd, "服务器收到: " . $data . "\n");
});
// 监听连接关闭事件
$server->on('Close', function ($server, $fd) {
    echo "客户端断开: fd=$fd\n";
});
// 服务器启动
$server->start();

完整的客户端测试代码

<?php
// TCP客户端测试代码
$client = stream_socket_client(
    "tcp://127.0.0.1:8080",
    $errno,
    $errstr,
    30
);
if (!$client) {
    echo "连接失败: $errstr ($errno)\n";
    exit;
}
echo "连接成功,发送数据...\n";
// 发送数据
fwrite($client, "你好,服务器!");
// 读取响应
$response = fread($client, 8192);
echo "服务器响应: " . $response . "\n";
// 关闭连接
fclose($client);

使用 Workerman(另一个常见方案)

<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// 创建TCP服务
$tcp_worker = new Worker("tcp://0.0.0.0:8080");
// 设置进程数
$tcp_worker->count = 4;
// 当客户端连接时触发
$tcp_worker->onConnect = function($connection) {
    echo "新连接\n";
};
// 当收到数据时触发
$tcp_worker->onMessage = function($connection, $data) {
    $connection->send("收到: " . $data);
};
// 当连接关闭时触发
$tcp_worker->onClose = function($connection) {
    echo "连接关闭\n";
};
// 启动服务
Worker::runAll();

性能对比

方案 并发能力 开发复杂度 扩展性 适用场景
stream_socket 简单 学习/小规模
pcntl多进程 中等 一般 中等规模
Swoole 中等 生产环境
Workerman 简单 生产环境

安装建议

对于生产环境,推荐使用Swoole:

# 安装Swoole
pecl install swoole
# 或使用Docker
docker run -p 8080:8080 -v $(pwd):/app php:8.2-cli \
  sh -c "docker-php-ext-install swoole && php /app/server.php"

注意事项

  1. 超时处理:设置合理的超时时间
  2. 错误处理:完善异常和错误处理机制
  3. 资源管理:及时释放连接和资源
  4. 安全防护:防止恶意攻击和资源耗尽
  5. 日志记录:记录重要操作和错误信息

选择哪种方案取决于你的具体需求,生产环境推荐使用Swoole或Workerman。

抱歉,评论功能暂时关闭!