写了一个 WebSocket 框架

前言

之前为了学习 go 写了两个小项目,fastcgi-gomysql-proxy 都是用了 go 来处理二进制包,从而实现一些协议(fastcgi 协议 和 mysql 协议)
之后感觉各种应用协议并不是高深复杂的东西,只要跟着协议走就能跑通它。这次有点好奇 websocket 是什么协议,和 socket 是什么关系,有空用 php 写一个小框架就明白了
这里先贴出 github 地址 websocket-php

首先搜索 websocket 协议,在 Laravel China 找到了 老司机带你用 PHP 实现 Websocket 协议 这篇文章,里面介绍了很多基础知识,对于第一次实现 websocket 有很大帮助,因此就不在这赘述了
而我想做一个稍微 “能用” 的东西。在实现这些功能的过程中,遇到了一些与 websocket 相关或不相关的问题,要好好记录一下

目标

跟 Workerman 一样启动一个服务,同时管理多个连接,为各个事件设置回调,就像这样

$server = new Server();
$server->on('message', function(Connection $client, Message $message) use ($server) {
    $server->sendMessage($client, Packet::MSG_TYPE_TXT, 'get ' . $message->content());
});
$server->start();

socket 连接的非阻塞模式

在调用 socket_acceptsocket_read 时,程序完全停止住了,这时如果第二个连接请求进来,则会超时。而 socket_set_nonblock 可以将这个连接设置为非阻塞模式。
这时与一般的阻塞模式有些许不同,在等待输入时,这两个函数不是阻塞住,而是直接返回 false ,需要做为 false 时的处理

$client = socket_accept($this->connection);
if ($client) {
    ...
}

Chrome 浏览器的包长限制

写完后在 index.html 测试,发现包长超过 255 字节就会报错
QQ截图20210927173148.png
我猜测 Chrome 限制了 websocket 协议包的长度,用 go 做客户端测试了一下,发现能正常传输,这也证实了猜想。所以要考虑分包的问题了

消息抽象 和 websocket 协议分包

使用 websocket 通信应该专注与传输的消息而忽略底层的分包操作,因此需要先抽象出一个 Message 类,一个消息可以由一个或多个包组成
无论是发送还是接受消息时,只需要遵守 websocket 协议分包即可,协议中有包有两个部分用于分包。第一个是第一位(FIN),1表示结束,0表示未结束。第二个是第 5 到 8 位(opcode),如果是 0 的话表示这个包是上个包的延续

输入抽象以方便测试

在编写的过程中总是要不断测试,开启服务,再开启客户端发送消息的测试方式太过麻烦。如果可以不开启服务,直接读取包的内容就好了
于是包的读取通过 Reader 类完成,把输入的内容存到文件以后,Server 可以通过 startReadFile 来完成一次文件读取,来模拟接收一个或多个消息

运行截图

测试过程:

  1. 发送长度达到需要分包的消息
  2. 同时打开多个连接发送消息
  3. 关闭一个连接
  4. 读取本地文件

网页截图

1.png

php 输出截图

2.png

读取本地文件

$server = new Server();
$server->on('message', function($conn, Message $msg) {
    echo $msg->length(), PHP_EOL, substr($msg->content(), $msg->length()-10, 10);
});
$server->startReadFile('test_bin_file');

微信截图_20210928095414.png

其他

最近在看 Laravel 的源码,也写了一些 笔记,有什么建议欢迎交流

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 2

建议把websocket 协议解析单独弄类库

2年前 评论
moodrain (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!