Swoole从入门到入土(16)——WebSocket服务器[事件]
WIKI:
问:websocket协议虽然和http协议不同,但是兼容于http协议,如何判断客户端连接使用的是http协议?
答:通过使用 $server->connection_info($fd) 获取连接信息,返回的数组中有一项为 websocket_status,根据此状态可以判断是否为 WebSocket 客户端。
---------- 正文的分割线 -------------
Swoole\WebSocket\Server继承自Swoole\Http\Server,所以websocket server支持http server和tcp server的所有事件。另外,新增加了以下3个事件:
· onMessage (必选)
· onOpen 和 onHandShake (可选)
事件详解
onHandShake:WebSocket 建立连接后进行握手。WebSocket 服务器会自动进行 handshake 握手的过程,如果用户希望自己进行握手处理,可以设置 onHandShake 事件回调函数。
onHandShake(Swoole\Http\Request $request, Swoole\Http\Response $response);
· onHandShake 事件回调是可选的,需要自行处理 handshake 的时候,再设置这个回调函数。如果您不需要 “自定义” 握手过程,那么不要设置该回调,用 Swoole 默认的握手即可。
· 设置 onHandShake 回调函数后不会再触发 onOpen 事件,需要应用代码自行处理
· onHandShake 中必须调用 response->status() 设置状态码为 101 并调用 response->end() 响应,否则会握手失败.
· 内置的握手协议为 Sec-WebSocket-Version: 13,低版本浏览器需要自行实现握手
· 可以使用 server->defer 调用 onOpen 逻辑
示例:
$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
// print_r( $request->header );
// if (如果不满足我某些自定义的需求条件,那么返回end输出,返回false,握手失败) {
// $response->end();
// return false;
// }
// websocket握手连接算法验证
$secWebSocketKey = $request->header['sec-websocket-key'];
$patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';
if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {
$response->end();
return false;
}
echo $request->header['sec-websocket-key'];
$key = base64_encode(
sha1(
$request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
true
)
);
$headers = [
'Upgrade' => 'websocket',
'Connection' => 'Upgrade',
'Sec-WebSocket-Accept' => $key,
'Sec-WebSocket-Version' => '13',
];
// WebSocket connection to 'ws://127.0.0.1:9502/'
// failed: Error during WebSocket handshake:
// Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket
if (isset($request->header['sec-websocket-protocol'])) {
$headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
}
foreach ($headers as $key => $val) {
$response->header($key, $val);
}
$response->status(101);
$response->end();
});
onOpen:当 WebSocket 客户端与服务器建立连接并完成握手后会回调此函数。
onOpen(Swoole\WebSocket\Server $server, Swoole\Http\Request $request);
· $request 是一个 HTTP 请求对象,包含了客户端发来的握手请求信息
· onOpen 事件函数中可以调用 push 向客户端发送数据或者调用 close 关闭连接
· onOpen 事件回调是可选的
onMessage:当服务器收到来自客户端的数据帧时会回调此函数。
onMessage(Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame)
· $frame 是 Swoole\WebSocket\Frame 对象,包含了客户端发来的数据帧信息
· onMessage 回调必须被设置,未设置服务器将无法启动
· 客户端发送的 ping 帧不会触发 onMessage,底层会自动回复 pong 包,也可设置 open_websocket_ping_frame 参数手动处理
关于Swoole\WebSocket\Frame $frame

· $frame->data 如果是文本类型,编码格式必然是 UTF-8,这是 WebSocket 协议规定的

示例:
面向过程写法:
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
global $server;//调用外部的server
// $server->connections 遍历所有websocket连接用户的fd,给所有用户推送
foreach ($server->connections as $fd) {
// 需要先判断是否是正确的websocket连接,否则有可能会push失败
if ($server->isEstablished($fd)) {
$server->push($fd, $request->get['message']);
}
}
});
$server->start();
面向对象写法:
class WebSocketTest
{
public $server; public function __construct()
{
$this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$this->server->on('open', function (Swoole\WebSocket\Server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
$this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$this->server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$this->server->on('request', function ($request, $response) {
// 接收http请求从get获取message参数的值,给用户推送
// $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
foreach ($this->server->connections as $fd) {
// 需要先判断是否是正确的websocket连接,否则有可能会push失败
if ($this->server->isEstablished($fd)) {
$this->server->push($fd, $request->get['message']);
}
}
});
$this->server->start();
}
} new WebSocketTest();
--------------------------- 我是可爱的分割线 ----------------------------
最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。
Swoole从入门到入土(16)——WebSocket服务器[事件]的更多相关文章
- Swoole学习(五)Swoole之简单WebSocket服务器的创建
环境:Centos6.4,PHP环境:PHP7 服务端代码 <?php //创建websocket服务器 $host = '0.0.0.0'; $port = ; $ws = new swool ...
- swoole创建websocket服务器
目录 1 安装准备 1.1 安装swoole前必须保证系统已经安装了下列软件 1.2 下载并解压 1.3 编译安装成功后,修改php.ini 2 构建Swoole基本实例 2.1 tcp服务器实例 2 ...
- 04.swoole学习笔记--webSocket服务器
<?php //创建webSocket服务器 $serv=); //获取请求 //on //open 建立连接 $serv:服务器 $request:客户端信息 $serv->on('op ...
- 实现一个websocket服务器-理论篇
本文是Writing WebSocket servers的中文文档,翻译自MDNWriting WebSocket servers.篇幅略长,个人能力有限难免有所错误,抛砖引玉共同进步. websoc ...
- 如何实现websocket服务器-理论篇
WebSocket 服务器简单来说就是一个遵循特殊协议监听服务器任意端口的tcp应用.搭建一个定制服务器的任务通常会让让人们感到害怕.然而基于实现一个简单的Websocket服务器没有那么麻烦. 一个 ...
- node实现一个WEBSOCKET服务器
早点时候翻译了篇实现一个websocket服务器-理论篇,简单介绍了下理论基础,本来打算放在一起,但是感觉太长了大家可能都看不下去.不过发现如果拆开的话,还是不可避免的要提及理论部分.用到的地方就简要 ...
- Erlang cowboy websocket 服务器
Erlang cowboy websocket 服务器 原文见于: http://marcelog.github.io/articles/erlang_websocket_server_cowboy_ ...
- 如何用Baas快速在腾讯云上开发小程序-系列1:搭建API & WEB WebSocket 服务器
版权声明:本文由贺嘉 原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/221059001487422606 来源:腾云阁 h ...
- 用C语言实现websocket服务器
Websocket Echo Server Demo 背景 嵌入式设备的应用开发大都依靠C语言来完成,我去研究如何用c语言实现websocket服务器也是为了在嵌入式设备中实现一个ip camera的 ...
- netty系列之:使用netty搭建websocket服务器
目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...
随机推荐
- Go-快速排序
package main import "fmt" // 快速排序 // 特征: // 1. 选定一个数,让这个数左边的比这个数小,右边比这个数大 // 2. 然后这个基数就是已经 ...
- Java进程内线程数量限制的相关学习
Java进程内线程数量限制的相关学习 背景 还是之前出现 cannot create native thread 的问题的后续 周末在家学习了下如何在容器外抓取dump. 也验证了下能否开启超过宿主机 ...
- [转帖]【SQL SERVER】锁机制
https://www.cnblogs.com/WilsonPan/p/12618849.html 锁定是 SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制. 基 ...
- [转帖]PG Exporter
http://v0.pigsty.cc/zh/docs/reference/kernel-optimize/ Exporter https://github.com/Vonng/pg_exporter ...
- 【转帖】dl.google.com的国内镜像源
dl.google.com不能访问 1.通过https://ping.chinaz.com/解析出dl.google.com的国内代理 2.C:\Windows\System32\drivers\et ...
- gpedit.msc 打不开
win10系统推出已有不短的时间了,朋友们也纷纷升级了win10系统,但是暴露的问题也是越来越多,比如win10系统打开运行输入gpedit.msc命令时却提示找不到文件.那出现win10打不开gpe ...
- 使用Navicat 进行MySql数据库同步功能
使用Navicat 进行MySql数据库同步功能 作者:胡德安 准备: 打开Navicat管理工具(比如Navicat Premium 15管理工具) 两个数据库第一个是源数据库A和要被同步的目标数据 ...
- 突破性的多语言代码大模型基CodeShell:引领AI编程新时代
突破性的多语言代码大模型基CodeShell:北京大学与四川天府银行联合打造,引领AI编程新时代 1.CodeShell简介 CodeShell是北京大学知识计算实验室联合四川天府银行AI团队研发的多 ...
- 8.1 Windows驱动开发:内核文件读写系列函数
在应用层下的文件操作只需要调用微软应用层下的API函数及C库标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头 ...
- 10.2 调试事件获取DLL装载
理解了如何通过调试事件输出当前进程中寄存器信息,那么实现加载DLL模块也会变得很容易实现,加载DLL模块主要使用LOAD_DLL_DEBUG_EVENT这个通知事件,该事件可检测进程加载的模块信息,一 ...