Hyperf WebSocket Server 实战指南:从零搭建高性能 WebSocket 服务
后端微服务【免费下载链接】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 的 WebSocket Server 进行了完整封装基于 hyperf/websocket-server 组件即可快速构建 WebSocket 应用并与 Hyperf 的依赖注入、路由、中间件、协程上下文等能力无缝集成。本文将以官方文档 WebSocket server 为核心脉络完整讲解从安装、服务配置、路由与中间件绑定到控制器编写、连接上下文、跨 Worker 主动推送以及WebSocket 中处理 HTTP 请求等进阶玩法的全过程并结合仓库源码剖析握手校验、消息分发、fd 收集器等底层实现帮助你写出可直接上线的 WebSocket 服务。安装组件WebSocket Server 是 Hyperf 的独立组件通过 Composer 安装composer require hyperf/websocket-server从组件 composer.json 可以看到该组件要求 PHP 8.2依赖hyperf/contract、hyperf/http-server、hyperf/context、hyperf/exception-handler、hyperf/coordinator等 Hyperf 核心组件并遵循 PSR-4 规范将Hyperf\WebSocketServer\命名空间映射到src/目录。组件通过 ConfigProvider.php 自动注册了两个监听器Listener\InitSenderListener初始化 Sender 的 WorkerId与Listener\OnPipeMessageListener处理跨 Worker 的管道消息安装后无需额外手动注册即可生效。配置 WebSocket Server在config/autoload/server.php中新增一个servers配置项即可声明一个 WebSocket 服务?php return [ servers [ [ name ws, type Server::SERVER_WEBSOCKET, host 0.0.0.0, port 9502, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_HAND_SHAKE [Hyperf\WebSocketServer\Server::class, onHandShake], Event::ON_MESSAGE [Hyperf\WebSocketServer\Server::class, onMessage], Event::ON_CLOSE [Hyperf\WebSocketServer\Server::class, onClose], ], ], ], ];各配置项含义如下配置项说明name服务名称路由与中间件配置都会以它为 key 关联到对应服务type服务类型WebSocket 使用Server::SERVER_WEBSOCKEThost/port监听地址与端口示例中 WebSocket 服务监听9502sock_typeSocket 类型SWOOLE_SOCK_TCP表示 TCP 协议callbacks三个关键事件的回调onHandShake握手、onMessage消息、onClose关闭这三个回调全部指向Hyperf\WebSocketServer\Server类的对应方法。从源码 Server.php 可见Server实现了OnHandShakeInterface、OnCloseInterface、OnMessageInterface并通过initCoreMiddleware()读取middlewares.{serverName}与exceptions.handler.{serverName}配置默认异常处理器为WebSocketExceptionHandler。配置路由目前 WebSocket 服务仅支持配置文件方式定义路由注解方式即将支持。在config/routes.php中使用Router::addServer()将路由注册到名为ws的服务上ws即config/autoload/server.php中 WebSocket Server 的name?php Router::addServer(ws, function () { Router::get(/, App\Controller\WebSocketController); });与 HTTP 路由不同WebSocket 路由最终指向的是一个控制器类而非具体方法。底层 CoreMiddleware.php 在handleFound()中会通过prepareHandler()解析出控制器类将其作为class属性写入响应对象如果路由不存在或容器中无对应控制器会抛出WebSocketHandShakeException。握手成功后该控制器类会被记录进 fd 收集器见下文后续的onMessage、onClose事件都会实例化同一个控制器来分发。配置中间件WebSocket 服务同样支持中间件在config/autoload/middlewares.php中以服务名ws为 key 配置?php return [ ws [ yourMiddleware::class ], ];这些中间件会在握手阶段onHandShake被执行。Server.php 的握手流程中coreMiddleware-dispatch()完成路由分发后会将全局中间件$this-middlewares与通过MiddlewareManager::get()获取的路由级中间件合并再交给HttpDispatcher统一调度——因此 WebSocket 的鉴权、限流等中间件逻辑与 HTTP 服务共用同一套中间件机制用法完全一致。创建控制器创建一个同时实现OnMessageInterface、OnOpenInterface、OnCloseInterface的控制器?php declare(strict_types1); namespace App\Controller; use Hyperf\Contract\OnCloseInterface; use Hyperf\Contract\OnMessageInterface; use Hyperf\Contract\OnOpenInterface; use Swoole\Http\Request; use Swoole\Server; use Swoole\Websocket\Frame; use Swoole\WebSocket\Server as WebSocketServer; class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface { public function onMessage($server, Frame $frame): void { $server-push($frame-fd, Recv: . $frame-data); } public function onClose($server, int $fd, int $reactorId): void { var_dump(closed); } public function onOpen($server, Request $request): void { $server-push($request-fd, Opened); } }三个回调接口分别对应 WebSocket 生命周期的三个阶段onOpen握手成功、连接建立后触发通常在此做初始化如记录在线用户onMessage收到客户端消息时触发$frame-data为消息内容$frame-fd为连接句柄onClose连接关闭时触发。在非协程风格异步风格的 Swoole Server 下Server.php 会在握手成功后通过defer()延迟执行onOpen在协程风格服务CoroutineServer、SwowServer下则通过wait()包裹并在子协程中预先设置好Context::FD保证onOpen也能正确读取连接上下文。另外onMessage 与 onClose 都会先从FdCollector中取出握手阶段记录的控制器类再实例化调用若 fd 不存在会直接返回并记录 warning 日志。启动服务执行启动命令即可看到 WebSocket Server 成功监听 9502 端口$ php bin/hyperf.php start [INFO] Worker#0 started. [INFO] WebSocket Server listening at 0.0.0.0:9502 [INFO] HTTP Server listening at 0.0.0.0:9501! 当 HTTP Server9501与 WebSocket Server9502同时监听时WebSocket 客户端通过两个端口都可以连接即连接ws://0.0.0.0:9501与ws://0.0.0.0:9502均有效。原因是Swoole\WebSocket\Server继承自Swoole\Http\Server天然兼容 HTTP 协议因此可以用 HTTP 方式完成所有 WebSocket 推送。如果希望 HTTP 服务不再受理 WebSocket 协议升级可以在config/autoload/server.php中给http服务加上open_websocket_protocol配置并设为false?php return [ // 无关配置已省略 servers [ [ name http, type Server::SERVER_HTTP, host 0.0.0.0, port 9501, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_REQUEST [Hyperf\HttpServer\Server::class, onRequest], ], settings [ open_websocket_protocol false, ] ], ] ];源码视角握手是如何完成的关于握手Security.php 封装了完整的协议校验逻辑isInvalidSecurityKey()用正则#^[/0-9A-Za-z]{21}[AQgw]$#校验客户端传来的sec-websocket-key并验证其 base64 解码后长度为 16 字节handshakeHeaders()则会生成Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Accept通过sha1(key 258EAFA5-E914-47DA-95CA-C5AB0DC85B11)签名等响应头。CoreMiddleware 将响应状态设置为101即完成了标准的 WebSocket 协议升级。连接上下文Connected ContextWebSocket 的onOpen、onMessage、onClose回调不会在同一个协程中触发因此它们之间无法直接使用协程上下文存储的数据。为此WebSocket Server 组件提供了Connected Context连接上下文其 API 与协程上下文一致但数据以fd为维度隔离天然解决每个连接各存一份数据的需求。?php declare(strict_types1); namespace App\Controller; use Hyperf\Contract\OnMessageInterface; use Hyperf\Contract\OnOpenInterface; use Hyperf\WebSocketServer\Context; use Swoole\Http\Request; use Swoole\Websocket\Frame; use Swoole\WebSocket\Server as WebSocketServer; class WebSocketController implements OnMessageInterface, OnOpenInterface { public function onMessage($server, Frame $frame): void { $server-push($frame-fd, Username: . Context::get(username)); } public function onOpen($server, Request $request): void { Context::set(username, $request-cookie[username]); } }从源码 Context.php 可以看到其实现原理set()内部通过CoContext::get(Context::FD)取得当前连接的 fd再将数据存入{fd}.{key}形式的键中get()/has()也按相同规则读取从而实现按连接隔离的存储。该上下文还提供destroy()、release()连接关闭时清理、copy()将某个连接的上下文复制到当前连接、override()、getOrSet()等能力。在 Server.php 的onClose中通过defer()延迟执行FdCollector::del($fd)与Context::release($fd)确保连接关闭后相关数据被及时回收。多 WebSocket Server 配置Nginx 负载均衡当需要部署多个 WebSocket Server 实例时可以通过 Nginx 的upstream做反向代理与负载均衡。官方示例配置如下# /etc/nginx/conf.d/ng_socketio.conf # multiple ws server upstream io_nodes { server ws1:9502; server ws2:9502; } server { listen 9502; # server_name your.socket.io; location / { proxy_set_header Upgrade websocket; proxy_set_header Connection upgrade; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header Host $host; # proxy_http_version 1.1; # Forward to multiple ws server proxy_pass http://io_nodes; } }注意 Nginx 代理 WebSocket 的关键在于必须设置Upgrade与Connection两个请求头如上所示否则协议升级无法完成。多个ws节点通过upstream统一对外客户端只需连接 Nginx 的 9502 端口。Sender跨 Worker 主动推送与断开WebSocket 连接归属于具体的 Worker 进程当你想在HTTP 服务中主动推送消息或断开连接时直接调用 Swoole 原生的push()是行不通的目标 fd 可能不在当前 Worker 内。此时应使用组件提供的Hyperf\WebSocketServer\Sender。Sender的工作机制先检查fd是否归属于当前 Worker若属于则直接发送否则通过PipeMessage将消息广播给其他所有 Worker由持有该 fd 的 Worker 完成实际发送。Sender.php 的__call()正是这一逻辑的入口——proxy()直接发送失败后会调用sendPipeMessage()向其余每个 Worker 发送SenderPipeMessage另一端由 OnPipeMessageListener 监听OnPipeMessage事件并再次调用proxy()完成投递。Sender支持两个方法push推送数据与disconnect断开连接。典型用法如下?php declare(strict_types1); namespace App\Controller; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\AutoController; use Hyperf\WebSocketServer\Sender; use function Hyperf\Coroutine\go; #[AutoController] class ServerController { #[Inject] protected Sender $sender; public function close(int $fd) { go(function () use ($fd) { sleep(1); $this-sender-disconnect($fd); }); return ; } public function send(int $fd) { $this-sender-push($fd, Hello World.); return ; } }除此之外Sender还提供pushFrame()用于推送FrameInterface数据帧可指定 opcode 与 finish 标志以及check($fd)方法通过connection_info()判断该连接是否处于WEBSOCKET_STATUS_ACTIVE状态判断连接是否仍然活跃。需要留意的是在协程风格服务server.type为CoroutineServer或SwowServer下Sender直接基于保存在responses中的连接对象发送无需跨进程通信。InitSenderListener会在 Worker 启动时调用setWorkerId()记录当前 WorkerId这是判断fd 是否归属当前 Worker的基础。在 WebSocket Server 中处理 HTTP 请求除了通过端口分离 HTTP 与 WebSocket 服务外还可以让 WebSocket 服务同时处理 HTTP 请求。由于server.servers.*.callbacks中的配置项都是单例需要先在config/autoload/dependencies.php中声明一个新的单例?php return [ HttpServer Hyperf\HttpServer\Server::class, ];然后修改 WebSocket 服务的callbacks配置在原有三个回调基础上追加Event::ON_REQUEST以下省略无关配置?php declare(strict_types1); use Hyperf\Server\Event; use Hyperf\Server\Server; return [ mode SWOOLE_BASE, servers [ [ name ws, type Server::SERVER_WEBSOCKET, host 0.0.0.0, port 9502, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_REQUEST [HttpServer, onRequest], Event::ON_HAND_SHAKE [Hyperf\WebSocketServer\Server::class, onHandShake], Event::ON_MESSAGE [Hyperf\WebSocketServer\Server::class, onMessage], Event::ON_CLOSE [Hyperf\WebSocketServer\Server::class, onClose], ], ], ], ];配置完成后即可在ws服务中直接添加 HTTP 路由。这种模式适用于同一端口同时提供 REST API 与 WebSocket 长连接的场景例如聊天室的鉴权接口与实时消息通道共用 9502 端口。由于Swoole\WebSocket\Server本身继承自Swoole\Http\Server这在协议层面是完全支持的。总结至此你已经完整掌握了 Hyperf 中 WebSocket Server 的核心用法服务声明在config/autoload/server.php中配置SERVER_WEBSOCKET类型服务并绑定onHandShake/onMessage/onClose回调路由与中间件通过Router::addServer(ws, ...)注册控制器路由通过middlewares.php的ws键绑定中间件握手阶段即完成路由分发与中间件调度生命周期控制控制器实现OnOpenInterface/OnMessageInterface/OnCloseInterface三接口配合Connected Context按连接隔离数据主动推送利用Sender在 HTTP 层跨 Worker 完成push/disconnect底层通过 PipeMessage 实现进程间协作扩展玩法通过open_websocket_protocol关闭 HTTP 端的 WebSocket 升级通过追加ON_REQUEST回调让 WebSocket 服务同时承载 HTTP 请求。如需进一步验证各环节行为可以查阅组件的单元测试ServerTest.php、ContextTest.php、SenderTest.php并结合 websocket-client 文档 编写客户端完成端到端联调。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Java-WebSocket完全指南从零构建高性能WebSocket客户端与服务器Java WebSocket完全指南从零构建高性能WebSocket客户端与服务器 引言为什么选择Java WebSocket 你是否正在寻找一个轻量级、后端WebSocket网络通信tchMaterial-parser一键把智慧教育平台在线电子课本下载成本地PDFtchMaterial parser一键把智慧教育平台在线电子课本下载成本地PDF 把国家中小学智慧教育平台的教材预览页网址粘进 tchMaterial pa后端微服务终极VibeVoice实时语音生成指南从零搭建WebSocket服务终极VibeVoice实时语音生成指南从零搭建WebSocket服务 VibeVoice是微软开源的前沿语音AI项目其 实时语音生成 功能能够实现约300毫语音音频人工智能大模型模型推理服务微调上一篇OpenChamber 隔离空间 Dispatcher 识别机制为何选择客户端前缀寻址而非服务端嗅探下一篇Chronos-2-Synth vs 传统模型为什么合成数据训练的时间序列模型更强大创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

NOCODE Context Engineering 实战指南:不用写一行代码,用协议壳、Pareto-lang 与场论驯服 AI 上下文窗口

NOCODE Context Engineering 实战指南:不用写一行代码,用协议壳、Pareto-lang 与场论驯服 AI 上下文窗口

文档教程知识库人工智能提示工程 【免费下载链接】Context-Engineering "Context engineering is the delicate art and science of filling the context window with just the right information for the next step." — Andrej Karpathy. A frontier, first-princi…

2026/10/8 7:47:35 阅读更多 →
Yaak 多语言切换教程:4 个文件给桌面 API 客户端加上中文界面

Yaak 多语言切换教程:4 个文件给桌面 API 客户端加上中文界面

Yaak 多语言切换教程:4 个文件给桌面 API 客户端加上中文界面 【免费下载链接】yaak The most intuitive desktop API client. Organize and execute REST, GraphQL, WebSockets, Server Sent Events, and gRPC 🦬 项目地址: https://gitcode.com/GitH…

2026/10/8 7:47:34 阅读更多 →
chsrc 的 rawstr4c 配置实战:以 Homebrew 换源 recipe 的 Markdown 模板与 C 字符串生成为例

chsrc 的 rawstr4c 配置实战:以 Homebrew 换源 recipe 的 Markdown 模板与 C 字符串生成为例

CLI开发工具 【免费下载链接】chsrc chsrc 全平台通用换源工具与框架. Change Source everywhere for every software 项目地址: https://gitcode.com/gh_mirrors/ch/chsrc 点击查看 免费下载 本文以 chsrc 仓库中 Homebrew 换源 recipe 的 rawstr4c 输入文档 为核…

2026/10/9 9:55:17 阅读更多 →

最新新闻

kernelbase.dll丢失报错详解:从DLL原理到SFC/DISM修复全攻略

kernelbase.dll丢失报错详解:从DLL原理到SFC/DISM修复全攻略

1. 先从报错入手:kernelbase.dll 丢失到底长什么样 1.1 这个文件是干什么的,为什么程序离不开它 如果你最近打开某个软件时,屏幕上突然跳出一句“由于找不到 kernelbase.dll,无法继续执行代码”,或者在启动 Windows 时…

2026/10/9 10:35:03 阅读更多 →
Linux IPC管道深度解析:匿名管道与FIFO的机制及实践

Linux IPC管道深度解析:匿名管道与FIFO的机制及实践

做日志采集模块那阵子,我接了一个让我印象很深的活儿:采集进程拿到的原始数据要源源不断交给另一个独立进程做过滤,两个进程之间没有网络,也没有共享的业务组件,唯一的需求就是“把数据从A顺利流到B”。我翻了一圈方案…

2026/10/9 10:35:03 阅读更多 →
Linux管道IPC全解:从匿名管道到FIFO、阻塞与SIGPIPE实战

Linux管道IPC全解:从匿名管道到FIFO、阻塞与SIGPIPE实战

做后端或者做 Linux 开发的人,迟早都要面对进程间通信(IPC)这个绕不开的话题。两个进程要协作,总得有个传数据的办法,管道就是我每次都要先拎出来讲清楚的一种 IPC 机制。它可能是 Unix 历史上最古老、看起来最简单、却…

2026/10/9 10:35:03 阅读更多 →
Access教学管理数据库实验全流程:建表、SQL查询与避坑指南

Access教学管理数据库实验全流程:建表、SQL查询与避坑指南

简介:这份《数据库及其应用》实验报告文档面向高校数据库课程学习者,尤其适合正在完成Access实验作业或准备课程设计的学生。内容围绕数据库设计、创建与应用展开,涵盖E-R模型构建、关系模型转换、表结构与字段属性定义、主键与参照完整性设置…

2026/10/9 10:35:03 阅读更多 →
CMOS图像传感器选型指南:从参数解读到样片验证的行业调研

CMOS图像传感器选型指南:从参数解读到样片验证的行业调研

简介:这份行业分析资料聚焦CMOS数字图像传感器领域,面向半导体、消费电子及投资研究从业者,帮助读者系统把握全球与中国市场的规模走势、竞争格局与技术演进方向。资源为单个PDF文档,压缩包约413KB,内容以数据表格与文…

2026/10/9 10:35:03 阅读更多 →
PerfDog性能测试有效测量方法论:从数据采集到根因归因

PerfDog性能测试有效测量方法论:从数据采集到根因归因

1. 这不是又一个“点几下就出报告”的工具教程PerfDog——这三个字最近在测试圈、开发组、甚至产品需求评审会上出现的频率,高得有点反常。某次和一位做App质量保障的同行吃饭,他掏出手机翻出刚跑完的PerfDog报告截图,第一句话不是“帧率稳了…

2026/10/9 10:34:02 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →