Swoole从入门到入土(20)——WebSocket服务器[协程版本]
本篇让我们先用一段示例代码开路:
<?php Co\run(function () { $server = new Co\Http\Server('0.0.0.0', 9501, false); $server->handle('/', function ($request, $ws) { $ws->upgrade(); while (true) { $frame = $ws->recv(); if ($frame === '') { $ws->push("bye bye"); $ws->close(); break; } else if ($frame === false) { echo "error : " . swoole_last_error() . "\n"; break; } else { if ($frame->data == 'close' || get_class($frame) === Swoole\WebSocket\CloseFrame::class) { $ws->push("bye bye"); $ws->close(); return; } $ws->push("Hello {$frame->data}!"); $ws->push("How are you, {$frame->data}?"); } } }); $server->start(); });
这一段代码中,我们以协程的形式实例化了协程版本的http服务器。什么?你忘了什么是协程?那请先看这里==》传送门(Swoole从入门到入土(8)——协程初探)
每当有个新的客户端使用websocket向服务端发起连接请求时,服务端都会调用handle中使用的回调函数(即,有几个客户端连接,就有几次回调函数的调用),这些函数共同运行在一个协程容器内部。在回调函数内部,基本的处理流程如下:
· $ws->upgrade():向客户端发送 WebSocket 握手消息
· while(true) 循环处理消息的接收和发送
· $ws->recv() 接收 WebSocket 消息帧
· $ws->push() 向对端发送数据帧
· $ws->close() 关闭连接
其中:$ws 是一个 Swoole\Http\Response 对象。
接下来,让我们一起看一下$ws中每个成员函数的使用方法。
1) 函数upgrade():发送 WebSocket 握手成功信息。此方法不要用于异步风格的服务器中。
Swoole\Http\Response->upgrade(): bool
2) 函数recv():接收 WebSocket 消息。此方法不要用于异步风格的服务器中,调用 recv 方法时会挂起当前协程,等待数据到来时再恢复协程的执行。
Swoole\Http\Response->recv(double timeout = -1): Swoole\WebSocket\Frame | false | string
返回值:
成功收到消息,返回 Swoole\WebSocket\Frame 对象
object(Swoole\WebSocket\Frame)#1 (4) { ["fd"] => int(0) ["data"] => NULL ["opcode"] => int(1) ["finish"] => bool(true) }
失败返回 false,请使用 swoole_last_error() 获取错误码
连接关闭返回空字符串
3) 函数push():发送 WebSocket 数据帧。此方法不要用于异步风格的服务器中,发送大数据包时,需要监听可写,因此会引起多次协程切换。
Swoole\Http\Response->push(string|object $data, int $opcode = 1, bool $finish = true): bool
· string|object $data:要发送的内容。若传入的 $data 是 Swoole\WebSocket\Frame 对象则其后续参数会被忽略,支持发送各种帧类型
· int $opcode:指定发送数据内容的格式 【默认为文本。发送二进制内容 $opcode 参数需要设置为 WEBSOCKET_OPCODE_BINARY】
· bool $finish:是否发送完成
4) 函数close():关闭 WebSocket 连接。此方法不要用于异步风格的服务器中,在 v4.4.15 以前版本会误报 Warning 忽略即可。
Swoole\Http\Response->close(): bool
最后,我们用群发的代码,来结束本篇内容。
看下面这一段代码时,我们要注意,因为所有的回调函数是运行在同一个协程容器内部,即存在同一个进程中(甚至是同一个线程中),所以全局变量在这里是可以安心使用的:
function getObjectId(\Swoole\Http\Response $response) { if (PHP_VERSION_ID < 70200) { $id = spl_object_hash($response); } else { $id = spl_object_id($response); } return $id; } Co\run(function () { $server = new Co\Http\Server('127.0.0.1', 9502, false); $server->handle('/websocket', function ($request, $ws) { $ws->upgrade(); global $wsObjects; $objectId = getObjectId($ws); $wsObjects[$objectId] = $ws; while (true) { $frame = $ws->recv(); if ($frame === '') { unset($wsObjects[$objectId]); $ws->close(); break; } else if ($frame === false) { echo 'error : ' . swoole_last_error() . "\n"; break; } else { if ($frame->data == 'close' || get_class($frame) === Swoole\WebSocket\CloseFrame::class) { unset($wsObjects[$objectId]); $ws->close(); return; } foreach ($wsObjects as $obj) { $obj->push("Server:{$frame->data}"); } } } }); $server->start(); });
本篇内容到此结束,下一篇我们将一起了解swoole的毫秒定时器。
--------------------------- 我是可爱的分割线 ----------------------------
最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!