- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
Hyperf 作为一款基于 Swoole 的协程框架,内置了对 TCP/UDP 服务的开箱即用支持:开发者只需编写一个事件回调类、在server.php配置中声明一个监听端口,即可启动一个高性能的 TCP/UDP Socket 服务,无需关心底层的事件循环与协程调度。本文将以官方文档docs/en/tcp-server.md为核心脉络,结合仓库中src/server与src/contract的源码实现,完整讲解 TCP 服务与 UDP 服务的搭建步骤、核心事件模型、底层 Server 构建原理以及多端口混合监听的实战技巧,帮助你快速掌握用 Hyperf 承载自定义二进制协议、物联网长连接或 UDP 数据采集服务的方法。
一、整体认知:Hyperf 的 TCP/UDP 服务能力
Hyperf 默认就具备创建 TCP/UDP 服务的能力。与 HTTP 服务类似,TCP/UDP 服务同样基于 Swoole 的Swoole\Server构建,因此天然继承 Swoole 的异步事件驱动、多 Worker 进程模型与协程调度能力。你只需要完成两件事:
- 编写一个回调类:实现框架约定的回调接口(如
OnReceiveInterface、OnPacketInterface),在对应方法中处理业务逻辑; - 编写一段配置:在项目的
config/autoload/server.php中声明一个servers节点,指定监听地址、端口、Socket 类型与事件回调。
框架会在启动时根据配置自动完成底层 Server 的创建、事件注册与生命周期管理,让开发者把精力完全集中在业务回调上。
在深入配置之前,先理解 Server 的类型体系。在 ServerInterface 中定义了三种服务类型常量:
| 常量 | 值 | 说明 |
|---|---|---|
ServerInterface::SERVER_HTTP | 1 | HTTP 服务 |
ServerInterface::SERVER_WEBSOCKET | 2 | WebSocket 服务 |
ServerInterface::SERVER_BASE | 3 | 基础 TCP/UDP 服务 |
TCP 与 UDP 服务使用的正是SERVER_BASE类型。在 Server::makeServer 中可以看到,当type为SERVER_BASE时,框架会直接new SwooleServer($host, $port, $mode, $sockType),即创建一个纯 Swoole 基础 Server,不附带任何 HTTP/WebSocket 协议解析能力,数据以原始字节流形式交给回调处理。
二、搭建 TCP 服务
2.1 创建 TcpServer 回调类
首先创建一个处理 TCP 数据收发的类,实现Hyperf\Contract\OnReceiveInterface接口:
<?php declare(strict_types=1); namespace App\Controller; use Hyperf\Contract\OnReceiveInterface; class TcpServer implements OnReceiveInterface { public function onReceive($server, int $fd, int $reactorId, string $data): void { $server->send($fd, 'recv:' . $data); } }OnReceiveInterface的定义位于 src/contract/src/OnReceiveInterface.php,其方法签名约束如下:
$server:当前 Swoole Server 实例(类型可能为Swoole\Server、协程风格的Swoole\Coroutine\Server\Connection或Hyperf\Server\Connection,与运行模式相关),可直接调用其send()等方法向客户端回写数据;$fd:客户端连接的文件描述符,用于标识一条 TCP 连接;$reactorId:Reactor 线程 ID(在 BASE 模式下通常为 0 或-1);$data:收到的原始数据内容(二进制或文本均可,Swoole 默认不解析协议,交给回调自行处理)。
上述示例的业务逻辑非常简单:收到任何数据后,向该连接回写recv:前缀加上原始数据,实现一个"回声 + 前缀"的迷你服务。
2.2 创建对应的服务配置
接着在config/autoload/server.php的servers数组中增加一个节点(以下仅展示与本服务相关的配置项,其余无关配置已省略):
<?php declare(strict_types=1); use Hyperf\Server\Server; use Hyperf\Server\Event; return [ // The following has removed other irrelevant configuration items 'servers' => [ [ 'name' => 'tcp', 'type' => Server::SERVER_BASE, 'host' => '0.0.0.0', 'port' => 9504, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ Event::ON_RECEIVE => [App\Controller\TcpServer::class, 'onReceive'], ], 'settings' => [ // Configure on demand ], ], ], ];各配置项含义与底层映射如下:
name:服务名称,用于在ServerManager中注册与区分多个服务。如果不显式设置,框架在 ServerConfig 中会尝试用数组键作为服务名(当键为非数字时);type:服务类型,这里必须是Server::SERVER_BASE,等价于ServerInterface::SERVER_BASE = 3;host:监听地址,0.0.0.0表示监听本机所有网卡;若仅需本机访问可填127.0.0.1。Port类的默认 host 为0.0.0.0(见 Port);port:监听端口,示例为9504,注意避免与系统已占用端口冲突;sock_type:Socket 类型,TCP 服务使用 Swoole 常量SWOOLE_SOCK_TCP,UDP 服务使用SWOOLE_SOCK_UDP;callbacks:事件回调映射,Event::ON_RECEIVE => [类名, 方法名]声明收到数据时调用哪个类的哪个方法;settings:Swoole Server 运行参数(如worker_num、open_length_check等),按需配置,留空数组即可。
一个容易被忽略的细节:当
type为SERVER_BASE时,Port::filter 会自动向 settings 中合并两个默认值——open_http2_protocol => false与open_http_protocol => false,确保基础 TCP/UDP 端口不会被意外解析为 HTTP 协议。该行为在 PortTest 中有对应的单元测试验证。
2.3 编写客户端进行联调
配置完成后启动服务(php bin/hyperf.php start),即可用一个最朴素的 Swoole 客户端验证服务是否工作:
<?php $client = new \Swoole\Client(SWOOLE_SOCK_TCP); $client->connect('127.0.0.1', 9504); $client->send('Hello World.'); $ret = $client->recv(); // recv:Hello World.当服务端收到Hello World.后,回调onReceive会执行$server->send($fd, 'recv:Hello World.'),因此客户端recv()拿到的返回值即为recv:Hello World.,与预期完全一致。
除了Swoole\Client,你同样可以使用系统自带的nc -v 127.0.0.1 9504、telnet 127.0.0.1 9504等命令行工具进行冒烟测试,或者用任意语言的 Socket 客户端发送原始字节流。
三、搭建 UDP 服务
UDP 是面向无连接的数据报协议,服务端通常以"收到一个数据包 → 处理 → 回发一个数据包"的方式工作。Hyperf 对 UDP 的支持同样简洁。
3.1 创建 UdpServer 回调类
<?php declare(strict_types=1); namespace App\Controller; use Hyperf\Contract\OnPacketInterface; class UdpServer implements OnPacketInterface { public function onPacket($server, $data, $clientInfo): void { var_dump($clientInfo); $server->sendto($clientInfo['address'], $clientInfo['port'], 'Server:' . $data); } }注意:官方文档特别说明——如果项目中不存在
OnPacketInterface接口文件(例如未安装hyperf/contract的早期版本),可以不实现该接口,只要配置正确,运行结果与实现了接口完全一致。也就是说,UDP 回调类的编写非常灵活,接口实现并非硬性约束。
OnPacketInterface的定义位于 src/contract/src/OnPacketInterface.php,回调参数含义如下:
$server:Swoole Server 实例;$data:收到的 UDP 数据包内容;$clientInfo:客户端信息数组,包含address(客户端 IP)、port(客户端端口)等字段,可用于定位发送方并决定回包目标。
示例逻辑中,先var_dump($clientInfo)打印客户端信息(方便观察联调时的来源地址),再通过$server->sendto($clientInfo['address'], $clientInfo['port'], 'Server:' . $data)向客户端回写一个带Server:前缀的数据包。
3.2 创建对应的服务配置
<?php declare(strict_types=1); use Hyperf\Server\Server; use Hyperf\Server\Event; return [ // The following has removed other irrelevant configuration items 'servers' => [ [ 'name' => 'udp', 'type' => Server::SERVER_BASE, 'host' => '0.0.0.0', 'port' => 9505, 'sock_type' => SWOOLE_SOCK_UDP, 'callbacks' => [ Event::ON_PACKET => [App\Controller\UdpServer::class, 'onPacket'], ], 'settings' => [ // Configure on demand ], ], ], ];与 TCP 配置相比,仅有两处不同:sock_type改为SWOOLE_SOCK_UDP,回调事件由Event::ON_RECEIVE改为Event::ON_PACKET。
3.3 UDP 客户端联调
启动服务后,可用Swoole\Client的 UDP 模式或nc -u 127.0.0.1 9505发送数据包验证:
<?php $client = new \Swoole\Client(SWOOLE_SOCK_UDP); $client->connect('127.0.0.1', 9505); $client->send('Hello UDP.'); $ret = $client->recv(); // Server:Hello UDP.由于 UDP 无连接特性,connect()在这里只是绑定对端地址,send()发送数据报后,服务端onPacket回调会收到数据与客户端信息,并通过sendto()回包。
四、事件模型:TCP/UDP 服务的回调事件总览
配置中的callbacks项本质上是"事件名 → 回调"的映射。框架在 Server::registerSwooleEvents 中遍历该映射,对每个合法 Swoole 事件调用$server->on($event, $callback)完成注册;若回调是[类名, 方法名]数组形式,框架会通过容器解析该类(从而获得完整的依赖注入与 AOP 能力),并自动调用initCoreMiddleware初始化核心中间件(若类实现了MiddlewareInitializerInterface)。
与 TCP/UDP 服务直接相关的核心事件定义在 src/server/src/Event.php 中,汇总如下:
| 事件常量 | 常量值 | 触发时机 | 说明 |
|---|---|---|---|
Event::ON_CONNECT | connect | 新客户端连接建立 | 监听连接进入事件,可用于连接鉴权、计数或初始化连接上下文 |
Event::ON_RECEIVE | receive | 收到 TCP 数据 | 监听数据接收事件,TCP 业务的核心入口 |
Event::ON_CLOSE | close | 客户端连接关闭 | 监听连接关闭事件,用于清理连接资源、下线通知等 |
Event::ON_PACKET | packet | 收到 UDP 数据报 | UDP 数据接收事件,UDP 业务的核心入口 |
Event类还定义了start、workerStart、workerStop、managerStart、shutdown、pipeMessage等进程级事件,供defaultCallbacks(见 Server::defaultCallbacks)自动注册以完成框架自身的引导流程(如启动协程运行时、初始化配置中心等)。因此即使你的callbacks只写了ON_RECEIVE一个事件,框架的生命周期引导也由内部默认回调保证。
一个更完整的 TCP 服务配置示例,同时注册连接进入与关闭事件,方便做在线状态管理:
'callbacks' => [ Event::ON_CONNECT => [App\Controller\TcpServer::class, 'onConnect'], Event::ON_RECEIVE => [App\Controller\TcpServer::class, 'onReceive'], Event::ON_CLOSE => [App\Controller\TcpServer::class, 'onClose'], ],对应的回调类方法(实现方式与onReceive相同,按接口约定实现即可):
public function onConnect($server, int $fd): void { // 连接建立,可记录 fd、客户端地址,或下发欢迎消息 $server->send($fd, 'Welcome!'); } public function onClose($server, int $fd): void { // 连接关闭,清理该 fd 对应的会话状态 }五、源码级原理:TCP/UDP Server 是如何被构建出来的
理解底层构建链路,有助于你在遇到多端口、多协议混跑场景时做出正确配置。整个启动流程由 Server 驱动,关键节点如下:
配置解析:
ServerConfig构造函数校验servers节点必须存在,否则抛出InvalidArgumentException('Config server.servers not exist.')(见 ServerConfig);随后将每个子项通过Port::build()构造成Port对象(见 Port::build)。端口排序:
sortServers()会优先将 WebSocket/HTTP 端口排在最前,普通 TCP/UDP 端口追加在末尾,保证 HTTP 相关 Server 作为主 Server 创建(见 Server::sortServers)。主 Server 创建:
initServers()遍历排序后的端口列表——第一个端口调用makeServer()依据type创建主 Swoole Server(SERVER_BASE对应Swoole\Server,见 Server::makeServer);后续端口调用addlistener()挂载为额外监听端口(见 Server::initServers)。这意味着你可以在同一进程内同时监听多个 TCP/UDP 端口,甚至与 HTTP/WebSocket 服务共存——只需在servers数组中追加多个节点即可。事件注册与参数合并:
callbacks经容器解析后注册到 Swoole;settings采用"全局 settings 与端口级 settings 合并,端口级优先"的规则(array_replace)。注册到 ServerManager:每个服务以
name为键注册进 ServerManager,业务代码可通过ServerManager::get($name)在任意协程内取回对应端口的 Server 实例,用于主动推送等场景。
多服务共存示例:若你想让一个进程同时提供 HTTP API(9501)与 TCP 长连接(9504),只需在
servers数组中同时保留 HTTP 节点与上述 TCP 节点。框架会自动把 HTTP 端口作为主 Server,TCP 端口作为附加监听端口。
六、进阶实践建议
- 自定义协议解析:TCP 是基于字节流的,
onReceive拿到的数据可能不是完整的一帧。可通过settings配置 Swoole 的协议选项(如open_length_check、package_length_offset、package_body_offset、package_max_length)开启自动分包,或在回调中自行实现粘包拆包逻辑。 - 连接资源管理:大量长连接场景下,务必在
ON_CLOSE中清理该fd对应的内存状态,避免连接泄漏;可用Coroutine\Channel或 Redis 记录在线 fd 列表。 - UDP 大数据包:UDP 单个数据报有 MTU 限制(通常 1472 字节左右),业务设计时需控制单包大小,或自行实现分片与重组协议。
- 性能调优:在
settings中按需设置worker_num、open_tcp_nodelay、backlog等参数;多 Worker 下注意fd归属与进程间通信,必要时使用sendMessage进行pipeMessage跨进程消息传递。 - 结合框架生态:TCP/UDP 回调类是经由容器解析的,因此可以在回调方法中通过构造函数注入任意 Hyperf 组件(如 Redis、数据库连接池、Logger),无缝复用框架能力。
七、小结
通过docs/en/tcp-server.md这份文档,可以总结出 Hyperf TCP/UDP 服务的四步套路:实现回调接口 → 编写回调方法 → 在server.php中声明SERVER_BASE类型端口 → 启动并联调。底层由 Server 统一完成 Swoole Server 的创建、事件注册与容器化回调绑定,配合 Event 中的ON_CONNECT/ON_RECEIVE/ON_CLOSE/ON_PACKET四个核心事件,足以覆盖绝大多数自定义 Socket 协议服务的开发需求。同时,由于支持多端口混合监听,TCP/UDP 服务可以很自然地与 HTTP/WebSocket 服务共存于同一 Hyperf 应用之中,是构建物联网网关、即时通讯、游戏服务端或二进制协议网关时的可靠选择。
- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
相关推荐
Hyperf 协程框架 TCP/UDP 服务开发实战指南
Hyperf 协程框架 TCP/UDP 服务开发实战指南 Hyperf 框架开箱即用地提供了基于 Swoole 的 TCP/UDP 网络服务能力,你不需要额外引
后端微服务Hyperf TCP/UDP 服务开发指南:从零搭建自定义网络协议服务
Hyperf TCP/UDP 服务开发指南:从零搭建自定义网络协议服务 Hyperf 协程框架默认内置了基于 Swoole 的 TCP/UDP 服务创建能力,只
后端Web框架微服务RPC框架异步编程pysheeet 实战指南:用 Python socket 构建 TCP/UDP 服务器(从 Echo 到零拷贝 sendfile)
pysheeet 实战指南:用 Python socket 构建 TCP/UDP 服务器(从 Echo 到零拷贝 sendfile) 本篇指南基于 pyshee
文档教程开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考