视频 | 一步步教你操作websocket通知案例「建议收藏」

视频 | 一步步教你操作websocket通知案例

大家好,又见面了,我是全栈君。

视频 | 一步步教你操作websocket通知案例「建议收藏」

                             (文末有福利)

websocket通知流程 解析

  1. 我们不能保证用户B和用户A都处于连接状态,但是通常情况下,用户B至少是连接状态,用户A不一定跟server保持连接;

  2. 任一用户都不止对应一个客户端。换言之,用户A和用户B都可能打开了多个tab页,对于一个tab页,就会有一个独立的fd标识,我们这里认为任一用户只有最新的fd有效,或者你可以认为用户所有的tab页的连接都有效;

  3. 因为没有用户系统,我们以get传递的参数uid为标识,uid=100视为用户A,uid=101视为用户B;

  4. 我们模拟的tab页包将会包含一个输入内容的文本框、一个输入目标uid的input和一个发送的按钮以满足需求。

操作流程分析:

  1. 用户A($_GET[‘uid’] = 100)在某个tab页的输入框输入”回复xxx的内容”字样后,点击发送

  2. 用户B($_GET[‘uid’] = 101)如果处于连接状态,则alert提醒用户B,他的评论被回复了

Server

后端启动:php server.php

class CommentServer
{
    private $_serv;
    public $key = '^manks.top&swoole$';
    // 用户id和fd对应的映射,key => value,key是用户的uid,value是用户的fd
    public $user2fd = [];


    public function __construct()
{
        $this->_serv = new swoole_websocket_server("127.0.0.1", 9501);
        $this->_serv->set([
            'worker_num' => 1,
            'heartbeat_check_interval' => 60,
            'heartbeat_idle_time' => 125,
        ]);
        $this->_serv->on('open', [$this, 'onOpen']);
        $this->_serv->on('message', [$this, 'onMessage']);
        $this->_serv->on('close', [$this, 'onClose']);
    }


    /**
     * @param $serv
     * @param $request
     * @return mixed
     */
    public function onOpen($serv, $request)
{
        // 连接授权
        $accessResult = $this->checkAccess($serv, $request);
        if (!$accessResult) {
            return false;
        }
        // 始终把用户最新的fd跟uid映射在一起
        if (array_key_exists($request->get['uid'], $this->user2fd)) {
            $existFd = $this->user2fd[$request->get['uid']];
            $this->close($existFd, 'uid exists.');
            $this->user2fd[$request->get['uid']] = $request->fd;
            return false;
        } else {
            $this->user2fd[$request->get['uid']] = $request->fd;
        }
    }


    /**
     * @param $serv
     * @param $frame
     * @return mixed
     */
    public function onMessage($serv, $frame)
{
        // 校验数据的有效性,我们认为数据被`json_decode`处理之后是数组并且数组的`event`项非空才是有效数据
        // 非有效数据,关闭该连接
        $data = $frame->data;
        $data = json_decode($data, true);
        if (!$data || !is_array($data) || empty($data['event'])) {
            $this->close($frame->fd, 'data format invalidate.');
            return false;
        }
        // 根据数据的`event`项,判断要做什么,`event`映射到当前类具体的某一个方法,方法不存在则关闭连接
        $method = $data['event'];
        if (!method_exists($this, $method)) {
            $this->close($frame->fd, 'event is not exists.');
            return false;
        }
        $this->$method($frame->fd, $data);
    }
    public function onClose($serv, $fd)
{
        echo "client {$fd} closed.\n";
    }


    /**
     * 校验客户端连接的合法性,无效的连接不允许连接
     * @param $serv
     * @param $request
     * @return mixed
     */
    public function checkAccess($serv, $request)
{
        // get不存在或者uid和token有一项不存在,关闭当前连接
        if (!isset($request->get) || !isset($request->get['uid']) || !isset($request->get['token'])) {
            $this->close($request->fd, 'access faild.');
            return false;
        }
        $uid = $request->get['uid'];
        $token = $request->get['token'];
        // 校验token是否正确,无效关闭连接
        if (md5(md5($uid) . $this->key) != $token) {
            $this->close($request->fd, 'token invalidate.');
            return false;
        }
        return true;
    }


    /**
     * @param $fd
     * @param $message
     * 关闭$fd的连接,并删除该用户的映射
     */
    public function close($fd, $message = '')
{
        // 关闭连接
        $this->_serv->close($fd);
        // 删除映射关系
        if ($uid = array_search($fd, $this->user2fd)) {
            unset($this->user2fd[$uid]);
        }
    }


    public function alertTip($fd, $data)
{
        // 推送目标用户的uid非真或者该uid尚无保存的映射fd,关闭连接
        if (empty($data['toUid']) || !array_key_exists($data['toUid'], $this->user2fd)) {
            $this->close($fd);
            return false;
        }
        $this->push($this->user2fd[$data['toUid']], ['event' => $data['event'], 'msg' => '收到一条新的回复.']);
    }
    /**
     * @param $fd
     * @param $message
     */
    public function push($fd, $message)
{
        if (!is_array($message)) {
            $message = [$message];
        }
        $message = json_encode($message);
        // push失败,close
        if ($this->_serv->push($fd, $message) == false) {
            $this->close($fd);
        }
    }


    public function start()
{
        $this->_serv->start();
    }
}


$server = new CommentServer;
$server->start();

前端页面 client.php

<div>
    发送内容:<textarea name="content" id="content" cols="30" rows="10"></textarea><br>
    发送给谁:<input type="text" name="toUid" value="" id="toUid"><br>
    <button onclick="send();">发送</button>
</div>


<script>
    var ws = new WebSocket("ws://127.0.0.1:9501?uid=<?php echo $uid ?>&token=<?php echo $token; ?>");
    ws.onopen = function(event) {
    };
    ws.onmessage = function(event) {
        var data = event.data;
        data = eval("("+data+")");
        if (data.event == 'alertTip') {
            alert(data.msg);
        }
    };
    ws.onclose = function(event) {
        console.log('Client has closed.\n');
    };


    function send() {
        var obj = document.getElementById('content');
        var content = obj.value;
        var toUid = document.getElementById('toUid').value;
        ws.send('{"event":"alertTip", "toUid": '+toUid+'}');
    }
</script>

一步一步教你操作

视频 | 一步步教你操作websocket通知案例「建议收藏」

回复关键字获取资源链接:

wokerman实战之PHP在线客服:wokerman

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111875.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 提问智慧的oracle问题

    提问智慧的oracle问题提问的智慧Oracle版0。尝试在google,论坛,metalink,onlinedocument里搜索。1。写清楚你的执行log,报错信息,写清楚DBversion,OS 2。Instance方面的问题,请贴出alertlog3。network的问题,贴出server的listener.ora,sqlnet.ora并运行lsnrctlservice,贴出cl

  • spring boot 系列之六:深入理解spring boot的自动配置[通俗易懂]

    我们知道,springboot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子:Spring的JdbcTemplate是不是在Classpath里面?如果是,并

  • JNZ是什么指令_pushf指令

    JNZ是什么指令_pushf指令书中127页有这么一段,movdx,0x1f7.waits:inal,dxandal,0x88cmpal,0x08jnz.waits最后一句的jnz完全可以用jne代替,那jnz/jne,jz/je区别到底是什么呢?刚想到这个问题时候我有点懵,明明都是由ZF标志位决定的啊,有啥不一样啊QAQjnz指令,if(ZF!=0)则跳转,在

    2022年10月23日
  • webstorm 插件安装

    webstorm 插件安装1.打勾的表示已经安装2.没有安装的插件,可以在plugins搜索,在右边搜索结果里点install,然后重启webstorm3.这里有常用插件http://blog.csdn.net/xs20691718/article/details/52269027转载于:https://www.cnblogs.com/linsx/p/7770623.html…

  • Android之ViewStub的简单使用

    Android之ViewStub的简单使用1.viewstub就是动态加载试图;也就是在我们的app启动绘制页面的时候,他不会绘制到view树中;当在代码中执行inflate操作后,她才会被添加到试图中。其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:l…

  • 图形数字推理1000题及答案_小学奥数图形推理题

    图形数字推理1000题及答案_小学奥数图形推理题事情是这样滴!一个小伙伴在这两天提出一个问题如下:考虑到数字推理是浙江省考每年的必考题,图形题在去年的浙江省考中考查了四题。而图形题相较于分数数列、递推数列、多级数列等常见纯数字数列来说,在没有掌握一些常见技巧的前提下确实无从下手。这两天经过对图形题的系统性梳理发现其中有一些可操作的技巧与方法,希望能够帮助到即将踏入战场的浙江的小伙伴。当然,除了浙江的小伙伴之外,一些自主命题省份,如江苏、广东、吉…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号