首頁  >  文章  >  php框架  >  Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

藏色散人
藏色散人轉載
2020-10-20 13:43:592887瀏覽

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

WebSocket 用戶端訊息的解析

#前面我們示範了當客戶端連線服務端,會觸發連線事件,事件中我們要求傳回目前客戶端的fd。當客戶端傳送訊息給服務端,服務端會根據我們的規則將訊息傳送給指定fd 的客戶端:

app/listener/WsConnect.php

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

app/listener/ WsTest.php

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

客戶端執行上述兩個事件後,控制台列印出以下資訊:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

返回訊息前面有一些數字,40、 42都代表什麼意義呢?

因為我們使用的擴充功能是基於 SocketIO 協定的,這些數字可以理解為協定的代號。

開啟/vendor/topthink/think-swoole/src/websocket/socketio/Packet.php ,有以下內容:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

上面是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(&#39;连接成功&#39;);
    }
    //数据返回的解析
    function mycallback(data){
        var start = data.indexOf(&#39;[&#39;) // 第一次出现的位置
        var start1 = data.indexOf(&#39;{&#39;)
        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(&#39;连接断开&#39;);
    }
    function send()
{
        var message = document.getElementById(&#39;message&#39;).value;
        var to = document.getElementById(&#39;to&#39;).value;
        console.log("准备给" + to + "发送数据:" + message);
        ws.send(JSON.stringify([&#39;test&#39;,{
            to:to,
            message:message
        }])); //发送的数据必须是 [&#39;test&#39;,数据] 这种格式
    }
</script>
</body>
</html>

解析後的資料:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

使用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: [&#39;websocket&#39;]});
    socket.on(&#39;connect&#39;, function(){
        console.log(&#39;connect success&#39;);
    });
    socket.on(&#39;close&#39;,function(){
       console.log(&#39;connect close&#39;)
    });
    //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(&#39;message&#39;).value;
        var to = document.getElementById(&#39;to&#39;).value;
        socket.emit(&#39;test&#39;, {
            //属性可自行添加
            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 app\listener;
class WsConnect
{
    /**
     * 事件监听处理
     *
     * @return mixed
     * 受用 WebSocket 客户端连接入口
     */
    public function handle($event)
{
        //实例化 Websocket 类
        $ws = app(&#39;\think\swoole\Websocket&#39;);
        //
        $ws -> emit(&#39;sendfd&#39;,$ws -> getSender());
    }
}

app/listener/WsTest.php

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

開啟兩個客戶端,fd 分別是5、6:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

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 發送訊息:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

兩個客戶端都會受到訊息:

Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯

可見訊息已經被解析,因為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: [&#39;websocket&#39;]});

後端可以在連線事件中進行綁定:

app/listener/WsConnect.php

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

有了UID 與fd ,可以在每次連線成功後,更新資料庫,連線中斷後再清空用戶對因的fd。假如伺服器重啟,那麼二者的對應關係也就沒用了,所以不必存入資料庫,存入 Redis 最好,透過 Redis 的 Hash 來映射二者關係也是不錯的選擇。

以上是Think-Swoole之WebSocket客戶端訊息解析與使用SocketIO處理用戶UID與fd關聯的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:阿dai哥。如有侵權,請聯絡admin@php.cn刪除