Workerman通过事件驱动、非阻塞I/O模型高效处理高并发,利用多进程+单线程事件循环,结合Epoll等机制实现低资源、高响应的TCP服务。

Workerman实现TCP服务,核心在于创建一个
Worker
tcp://
onConnect
onMessage
onClose
要用Workerman构建一个TCP服务器,其实比想象中要直接。我们首先需要引入Workerman库,然后实例化一个
Worker
下面是一个简单的TCP服务器示例,它会接收客户端发送的任何消息,然后原样返回给客户端,也就是一个“echo”服务器:
<?php
require_once __DIR__ . '/vendor/autoload.php'; // 假设Workerman通过Composer安装
use Workerman\Worker;
// 创建一个Worker,监听1234端口,协议为TCP
$tcp_worker = new Worker("tcp://0.0.0.0:1234");
// 设置Worker进程数,这里设置为1,方便调试,生产环境可以根据CPU核心数调整
$tcp_worker->count = 1; 
// 当客户端连接时触发的回调
$tcp_worker->onConnect = function($connection) {
    echo "新客户端连接: " . $connection->getRemoteIp() . ":" . $connection->getRemotePort() . "\n";
    // 可以在这里发送欢迎消息
    // $connection->send("欢迎连接到我们的TCP服务!\n");
};
// 当客户端发送消息时触发的回调
$tcp_worker->onMessage = function($connection, $data) {
    echo "收到来自 " . $connection->getRemoteIp() . " 的消息: " . $data;
    // 将收到的数据原样返回给客户端
    $connection->send("你说了: " . $data);
};
// 当客户端断开连接时触发的回调
$tcp_worker->onClose = function($connection) {
    echo "客户端断开连接: " . $connection->getRemoteIp() . "\n";
};
// 当Worker进程启动时触发的回调(可选)
$tcp_worker->onWorkerStart = function($worker) {
    echo "TCP Worker 进程启动,监听端口: " . $worker->listen . "\n";
};
// 运行所有Worker
Worker::runAll();要运行这段代码,你需要:
composer require workerman/workerman
tcp_server.php
php tcp_server.php start
php tcp_server.php start -d
然后,你可以使用
telnet
127.0.0.1:1234
Workerman在处理高并发连接方面的能力,确实是它的一大亮点,这背后主要得益于其独特的事件驱动、非阻塞I/O模型。我个人觉得,理解这一点对于构建稳定、高性能的服务至关重要。
它并不是像传统的多线程/多进程模型那样,每个连接都分配一个独立的线程或进程去处理,那样会带来大量的上下文切换开销和资源消耗。Workerman采用的是基于事件循环(Event Loop)的单线程(或多进程,但每个进程内仍是单线程)模型。
具体来说,当一个客户端连接过来时,Workerman并不会立即为其创建一个新的线程或进程。相反,它会注册这个连接到一个事件监听器(比如Linux下的Epoll、BSD下的Kqueue或更通用的Select)中。当这个连接有数据可读、可写或者断开时,事件监听器会通知Workerman主循环,然后Workerman会调用预先注册好的回调函数(比如
onMessage
这种机制有几个显著的优点:
当然,如果你的业务逻辑是CPU密集型的,比如需要进行大量的计算,那么一个单进程可能会成为瓶颈。Workerman通过
$worker->count
count
count
在TCP层面,数据传输本身就是可靠的,它保证了数据包的顺序和完整性。但这里我们讨论的“可靠传输”更多是指应用层的数据包完整性,以及如何定义我们自己的通信规则,也就是自定义协议。这其实是构建任何非HTTP长连接服务的关键。
我遇到过不少开发者,刚开始用TCP服务时,直接把数据往
$connection->send()
onMessage
实现自定义协议,通常有两种主流方式:
基于长度的协议: 这是最常用也最稳健的方式。简单来说,每个数据包都由两部分组成:一个固定长度的头部,里面包含整个数据包(包括头部和数据体)的长度;以及紧随其后的数据体。 当
onMessage
$connection->onMessage = function($connection, $buffer) {
    // 假设我们有一个缓存区来存储不完整的包
    static $recv_buffer = '';
    $recv_buffer .= $buffer;
    while (strlen($recv_buffer) >= 4) { // 至少有4个字节的长度信息
        $total_len = unpack('N', substr($recv_buffer, 0, 4))[1]; // 解析出大端字节序的4字节长度
        if (strlen($recv_buffer) < $total_len) {
            // 数据不完整,等待更多数据
            break;
        }
        // 得到一个完整的包
        $package_data = substr($recv_buffer, 4, $total_len - 4);
        // 移除已处理的包
        $recv_buffer = substr($recv_buffer, $total_len);
        // 在这里处理 $package_data,例如解析JSON
        echo "收到完整数据包: " . $package_data . "\n";
        $connection->send("服务器已收到你的数据包。\n");
    }
};发送时,你需要先计算出数据体的长度,然后将其编码成4字节(例如用
pack('N', $length)基于分隔符的协议: 这种方式是数据包以一个特殊的、不会出现在数据体中的分隔符结尾。例如,很多简单的文本协议会用
\n
\r\n
onMessage
Text
\n
new Worker("text://0.0.0.0:1234")onMessage
无论哪种方式,关键在于客户端和服务器必须遵循相同的协议约定。没有这个约定,数据就无法正确解析。在实际项目中,我倾向于使用长度协议,因为它对二进制数据和包含特殊字符的文本数据兼容性更好,也更不容易出错。
将Workerman TCP服务从开发环境推向生产环境,需要考虑的细节还真不少。这不仅仅是把代码放上去那么简单,更关乎服务的稳定性、可靠性和安全性。
守护进程化运行: 在生产环境中,我们肯定不希望服务在终端关闭后就停止。Workerman支持守护进程模式运行,只需在启动命令后加上
-d
php your_server.php start -d
进程管理工具: 仅仅守护进程化还不够,服务崩溃了怎么办?服务器重启了怎么办?这时候就需要进程管理工具,比如
Supervisor
Systemd
Supervisor
日志记录与错误处理: 生产环境的服务,任何错误都不能悄无声息地发生。
onWorkerStart
onMessage
try-catch
系统资源与性能监控: 密切关注服务器的CPU、内存、网络I/O以及Workerman自身的连接数。
ulimit -n
status
php your_server.php status
安全性:
代码更新与平滑重启: 部署新代码时,我们希望服务能够不中断地更新。Workerman支持平滑重启 (
php your_server.php reload
这些注意事项,都是我在实际项目中踩过坑、总结出来的经验。每一点都关乎服务的健壮性,绝不能掉以轻心。
以上就是Workerman怎么实现TCP服务?WorkermanTCP服务器示例?的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号