视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Think-Swoole之WebSocket客户端消息解析与使用SocketIO处理用户UID与fd关联
2020-11-02 18:01:55 责编:小采
文档

WebSocket 客户端消息的解析

前面我们演示了当客户端连接服务端,会触发连接事件,事件中我们要求返回当前客户端的 fd。当客户端发送消息给服务端,服务端会根据我们的规则将消息发送给指定 fd 的客户端:

app/listener/WsConnect.php

<?php
declare (strict_types = 1);
namespace applistener;
class WsConnect
{
 /**
 * 事件监听处理
 *
 * @return mixed
 * 受用 WebSocket 客户端连接入口
 */
 public function handle($event)
{
 //实例化 Websocket 类
 $ws = app('	hinkswooleWebsocket');
 //
 $ws -> emit('sendfd',$ws -> getSender());
 }
}

app/listener/WsTest.php

<?php
declare (strict_types = 1);
namespace applistener;
use 	hinkswooleWebsocket;
class WsTest
{
 /**
 * 事件监听处理
 *
 * @return mixed
 */
 public function handle($event,Websocket $ws)
{
 $ws -> to(intval($event['to'])) -> emit('testcallback',$event['message']);
 }
}

客户端执行上述两个事件后,控制台打印出以下信息:

返回信息前面有一些数字,40、42都代表什么意义呢?

因为我们使用的扩展是基于 SocketIO 协议的,这些数字可以理解为协议的代号。

打开 /vendor/topthink/think-swoole/src/websocket/socketio/Packet.php ,有以下内容:

上面是 Socket 类型,下面是引擎,前后两个代号上下拼凑得到:

40:”MESSAGE CONNECT”
42:”MESSAGE EVENT”

结合这些代码,能知道 SocketIO 中消息的大体运作情况。

通过控制台打印出的消息,我们发现这些消息不能直接拿到使用,需要进行截取处理:

test.html

<!DOCTYPE HTML>
<html>
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
消息:<input type="text" id="message">
接收者:<input type="text" id="to">
<button onclick="send()">发送</button>
<script>
 var ws = new WebSocket("ws://127.0.0.1:9501/");
 ws.onopen = function(){
 console.log('连接成功');
 }
 //数据返回的解析
 function mycallback(data){
 var start = data.indexOf('[') // 第一次出现的位置
 var start1 = data.indexOf('{')
 if(start < 0){
 start = start1;
 }
 if(start >= 0 && start1 >= 0){
 start = Math.min(start,start1);
 }
 if(start >= 0){
 console.log(data);
 var json = data.substr(start); //截取
 var json = JSON.parse(json);
 console.log(json);
 }
 }
 ws.onmessage = function(data){
 // console.log(data.data);
 mycallback(data.data);
 }
 ws.onclose = function(){
 console.log('连接断开');
 }
 function send()
{
 var message = document.getElementById('message').value;
 var to = document.getElementById('to').value;
 console.log("准备给" + to + "发送数据:" + message);
 ws.send(JSON.stringify(['test',{
 to:to,
 message:message
 }])); //发送的数据必须是 ['test',数据] 这种格式
 }
</script>
</body>
</html>

解析后的数据:

使用 SocketIO 处理消息业务

SocketIO 的相关知识可以查看文档,重点看客户端方面知识:

https://www.w3cschool.cn/socket/socket-k49j2eia.html

iotest.html

<!DOCTYPE HTML>
<html>
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
消息:<input type="text" id="message">
接收者:<input type="text" id="to">
<button onclick="send()">发送</button>
<script src="./socketio.js"></script>
<script>
 //http 协议
 var socket = io("http://127.0.0.1:9501", {transports: ['websocket']});
 socket.on('connect', function(){
 console.log('connect success');
 });
 socket.on('close',function(){
 console.log('connect close')
 });
 //send_fd 为自定义的场景值,和后端对应
 socket.on("sendfd", function (data) {
 console.log(data)
 });
 //testcallback 为自定义的场景值,和后端对应
 socket.on("testcallback", function (data) {
 console.log(data)
 });
 function send() {
 var message = document.getElementById('message').value;
 var to = document.getElementById('to').value;
 socket.emit('test', {
 //属性可自行添加
 to:to,
 message:message
 })
 }
</script>
</body>
</html>

var socket = io("http://127.0.0.1:9501", {transports: ['websocket']}); 中第二个参数指明要升级的协议。

app/listener/WsConnect.php

<?php
declare (strict_types = 1);
namespace applistener;
class WsConnect
{
 /**
 * 事件监听处理
 *
 * @return mixed
 * 受用 WebSocket 客户端连接入口
 */
 public function handle($event)
{
 //实例化 Websocket 类
 $ws = app('	hinkswooleWebsocket');
 //
 $ws -> emit('sendfd',$ws -> getSender());
 }
}

app/listener/WsTest.php

<?php
declare (strict_types = 1);
namespace applistener;
use 	hinkswooleWebsocket;
class WsTest
{
 /**
 * 事件监听处理
 *
 * @return mixed
 */
 public function handle($event,Websocket $ws)
{
// $ws -> to(intval($event['to'])) -> emit('testcallback',$event['message']);
 $ws -> to(intval($event['to'])) -> emit('testcallback',[
 'form' => [
 'id' => 10,
 'fd' => $ws -> getSender(),
 'nickname' => '张三'
 ],
 'to' => [
 'id' => 11,
 'fd' => intval($event['to']),
 'nickname' => '李四'
 ],
 'massage' => [
 'id' => 888,
 'create_time' => '2020-03-13',
 'content' => $event['message']
 ]
 ]);
 }
}

开启两个客户端,fd 分别是5、6:

WsConnect.php 中,有 $ws -> emit('sendfd',$ws -> getSender()); 发送 fd 消息对应的场景值是 “sendfd” ,在 iotest.html 中,有socket.on("sendfd", function (data) {console.log(data)}); 这段代码,其中也有场景值 “sendfd”,这行代码可以直接获取对应场景值的信息,所以控制台上会打印出 fd 值。

用 fd 5 向 fd 6 发送信息:

两个客户端均会受到信息:

可见消息已经经过解析,因为 WsTest.php 中 发送消息指定场景值 testcallback,iotest.html 中通过 socket.on("testcallback", function (data){console.log(data)}); 可直接获取解析过的结果。

这就看出了 SocketIO 在客户端消息接收方面的便捷之处了。

用户 UID 和客户端 fd 的绑定

前面的例子中,都是通过指定 fd 来向客户端发送消息,实际场景中,我们不可能通过 fd 确定发送对象,因为 fd 不是固定不变的,因此需要将用户的 UID 与客户端的 fd 进行绑定,进而可以通过选择用户,来确定 fd 完成消息的发送。

只需要将前端页面的 HTTP 连接中增加 UID 参数即可:

test.html

var ws = new WebSocket("ws://127.0.0.1:9501/?uid=1");

iotest.html

var socket = io("http://127.0.0.1:9501?uid=1", {transports: ['websocket']});

后端可以在连接事件中进行绑定:

app/listener/WsConnect.php

<?php
declare (strict_types = 1);
namespace applistener;
class WsConnect
{
 /**
 * 事件监听处理
 *
 * @return mixed
 * 受用 WebSocket 客户端连接入口
 */
 public function handle($event)
{
 // $event 为请求对象
 //实例化 Websocket 类
 $ws = app('	hinkswooleWebsocket');
 //获取 uid
 $uid = $event -> get('uid');
 //获取 fd
 $fd = $ws -> getSender();
 //获取到 uid 和 fd 后,可以存数据库,内存或者 redis
 $ws -> emit('sendfd',[
 'uid' => $uid,
 'fd' => $fd
 ]);
 }
}

有了 UID 与 fd ,可以在每次连接成功后,更新数据库,连接断开后再清空用户对因的 fd。假如服务器重启,那么二者的对应关系也就没用了,所以不必存入数据库,存入 Redis 最好,通过 Redis 的 Hash 来映射二者关系也是不错的选择。

下载本文
显示全文
专题