WIKI:php
问:websocket协议虽然和http协议不一样,可是兼容于http协议,如何判断客户端链接使用的是http协议?web
答:经过使用 $server->connection_info($fd) 获取链接信息,返回的数组中有一项为 websocket_status,根据此状态能够判断是否为 WebSocket 客户端。算法
---------- 正文的分割线 -------------编程
Swoole\WebSocket\Server继承自Swoole\Http\Server,因此websocket server支持http server和tcp server的全部事件。另外,新增长了如下3个事件:数组
· onMessage (必选)浏览器
· onOpen 和 onHandShake (可选)服务器
事件详解websocket
onHandShake:WebSocket 创建链接后进行握手。WebSocket 服务器会自动进行 handshake 握手的过程,若是用户但愿本身进行握手处理,能够设置 onHandShake 事件回调函数。socket
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 逻辑tcp
示例:
$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();
--------------------------- 我是可爱的分割线 ----------------------------
最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同窗点击连接,联系我吧。