☰
Hyperf 框架 TCP/UDP 服务实战指南:从零搭建基于 Swoole 的 Socket 服务器
2026/10/8 1:38:08 网站建设 项目流程
  • 后端
  • 微服务

【免费下载链接】hyperf

🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.

项目地址:https://gitcode.com/gh_mirrors/hy/hyperf
点击查看免费下载

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 进程模型与协程调度能力。你只需要完成两件事:

  1. 编写一个回调类:实现框架约定的回调接口(如OnReceiveInterface、OnPacketInterface),在对应方法中处理业务逻辑;
  2. 编写一段配置:在项目的config/autoload/server.php中声明一个servers节点,指定监听地址、端口、Socket 类型与事件回调。

框架会在启动时根据配置自动完成底层 Server 的创建、事件注册与生命周期管理,让开发者把精力完全集中在业务回调上。

在深入配置之前,先理解 Server 的类型体系。在 ServerInterface 中定义了三种服务类型常量:

常量值说明
ServerInterface::SERVER_HTTP1HTTP 服务
ServerInterface::SERVER_WEBSOCKET2WebSocket 服务
ServerInterface::SERVER_BASE3基础 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_CONNECTconnect新客户端连接建立监听连接进入事件,可用于连接鉴权、计数或初始化连接上下文
Event::ON_RECEIVEreceive收到 TCP 数据监听数据接收事件,TCP 业务的核心入口
Event::ON_CLOSEclose客户端连接关闭监听连接关闭事件,用于清理连接资源、下线通知等
Event::ON_PACKETpacket收到 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 驱动,关键节点如下:

  1. 配置解析:ServerConfig构造函数校验servers节点必须存在,否则抛出InvalidArgumentException('Config server.servers not exist.')(见 ServerConfig);随后将每个子项通过Port::build()构造成Port对象(见 Port::build)。

  2. 端口排序:sortServers()会优先将 WebSocket/HTTP 端口排在最前,普通 TCP/UDP 端口追加在末尾,保证 HTTP 相关 Server 作为主 Server 创建(见 Server::sortServers)。

  3. 主 Server 创建:initServers()遍历排序后的端口列表——第一个端口调用makeServer()依据type创建主 Swoole Server(SERVER_BASE对应Swoole\Server,见 Server::makeServer);后续端口调用addlistener()挂载为额外监听端口(见 Server::initServers)。这意味着你可以在同一进程内同时监听多个 TCP/UDP 端口,甚至与 HTTP/WebSocket 服务共存——只需在servers数组中追加多个节点即可。

  4. 事件注册与参数合并:callbacks经容器解析后注册到 Swoole;settings采用"全局 settings 与端口级 settings 合并,端口级优先"的规则(array_replace)。

  5. 注册到 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.

项目地址:https://gitcode.com/gh_mirrors/hy/hyperf
点击查看免费下载
上一篇:如何利用AIRealNet在10分钟内搭建AI图像检测系统:完整教程
下一篇:ParlAI 对话矛盾检测实战:DECODE 数据集加载、JSONL 格式解析与矛盾判定

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询