利用laravel-echo主动向服务端发送消息,实现在线状态管理

「手把手」利用laravel-echo主动向服务端发送消息,实现在线状态管理

之前在网上翻了半天,也没有找到关于如何 通过laravel-echo主动发送消息laravel-websockets中自定义控制器 的文章或教程。无奈之下只能翻laravel-echolaravel-websockets的源码了,小有收获。在此为有需要的朋友指个方向,少踩一个坑。

开始吧

书接上回利用websocket实现手机扫码登陆后,同步登陆信息到web端页面,我们已经实现服务器主动向网页发送消息的功能了。但是,在使用laravel-echo时,网页想主动向服务器发送消息该怎么做呢?首先,最简单的就是Ajax异步请求了,写起来很容易。还有一种方式就是通过websocket了,既然已经建立了 socket 连接,我们何不利用他来进行双向通信呢!这就是本文的重点内容:如何使用laravel-echo通过websocket进行反向通信(这里的「反向」指从网页向服务器发送消息)。

主要流程

简述

在上一篇 文章 的案例中,websocket 默认监听的地址是http://<your.host>:<wsPort>/app/<key>。url 中的/app/部分是pusher中规定的,目前还无法修改。<key> 指实例化Echo时的参数「key」,同时也是.env文件中的PUSHER_APP_KEY环境变量。后台控制器是laravel-websockets已经定义好的 websocket 控制器:BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler。现在我们要实现自己的控制器,来做在线状态管理,所以就需要改变 websocket 默认监听的地址和后台控制器。

修改前端监听地址

打开app/resources/views/hellow.blade.php视图文件,在初始化Echo中的参数部分加入wsPath变量。此时 websocket 监听的地址就会变为http://<your.host>:<wsPort><wsPath>/app/<key>

        window.Echo = new Echo({
            broadcaster: 'pusher',
            key: 'joker',
            // 在 socket 链接中设置 url 路径
            wsPath: '/liam/hao',
			wsHost: location.hostname,
            wsPort: 2020,
            forceTLS: false,
        });

注意:wsPath变量一定要用 “/” 开头,否则会报错的

后端添加新的路由

因为前端改变了监听路由,对应的后端,也需要新增一个对应的路由。我们打开routes/web.php文件,加入以下新路由:

Route::get('/login', function () {
    return view('login');
});
// 这个是 laravel-websockets 提供的门面方法,用来注册自定义的 websocekt 路由
// WebSocketsRouter 绑定的实际对象是 BeyondCode\LaravelWebSockets\Server\Router 有兴趣的可以瞅瞅
\BeyondCode\LaravelWebSockets\Facades\WebSocketsRouter::webSocket('/liam/hao/app/{appKey}', \App\Http\Controllers\MyWebsocketHandler::class);

创建自定义控制器

上面的路由中绑定了一个App\Http\Controllers\MyWebsocketHandler类,现在我们就来实现这个类:

> php artisan make:controller MyWebsocketHandler
Controller created successfully.

执行上面的命令后,我们会在app/Http/Controllers文件夹中找到MyWebsocketHandler.php文件,我们来进行一些修改:

<?php

namespace App\Http\Controllers;

use BeyondCode\LaravelWebSockets\WebSockets\Messages\PusherMessageFactory;
use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler;
use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;
// 注意这里要继承自 WebSocketHandler
class MyWebsocketHandler extends WebSocketHandler
{
    public function onMessage(ConnectionInterface $connection, MessageInterface $message)
    {
        var_dump(json_decode($message->getPayload()));

        $message = PusherMessageFactory::createForMessage($message, $connection, $this->channelManager);

        $message->respond();
    }

    public function onClose(ConnectionInterface $connection)
    {
        $this->channelManager->removeFromAllChannels($connection);

        var_dump('close');
    }
}

好了,我们现在来小测一下,看看新的路由有没有生效。重启laravel-websockets的 http 服务。打开浏览器的开发者工具,然后刷新页面。如果像下图一样,在命令行终端里看到了我们var_dump()的数据,那就说明新的路由和控制器已经连通了:

「手把手」利用laravel-echo主动向服务端发送消息,实现在线状态管理

注意:MyWebsocketHandler 并不是一般的 Controller,他需要继承自BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler。如果你需要控制更多逻辑,可直接实现Ratchet\WebSocket\MessageComponentInterface接口,并自己实现onOpen()onClose()onMessage()等方法。

前端发送消息(重点)

重点来了哈,虽说是重点,但代码很简单:

        Echo.channel('abcdefg.'+uuid)
        .listen('LoginedEvent', (e) => {
            console.log(e);
            var session_id = e.session_id;
            location.href = location.origin+'/hello?session_id='+session_id;
        });
		
		// 我们在这里放置一个定时器,每三秒钟向服务器发送一条数据
		setInterval(function(){
			// 这里新增一个向服务端发送消息的方法
			// 第一个参数是事件名,这个可以随意写,不需要与 Laravel 中做对应
			// 第二个参数是具体数据,这个就更随意了
            Echo.connector.pusher.send_event('hi_girl', {
                my_name: 'LiamHao',
                my_height: 180,
            });
        }, 3000);

是不是很简单,我们再来小测一下,看看服务端接收到的数据是什么样子的:

「手把手」利用laravel-echo主动向服务端发送消息,实现在线状态管理

后端已经接收到数据了。做到这里,想必有些基础的朋友应该已经可以做自己想做的事情了。

设置在线状态

这里我们就不做太复杂了数据库操作了,还是老样子,以最简单的方式,用 缓存 做记录吧。我们在App\Http\Controllers\MyWebsocketHandleronMessage()onClose()方法中分别加入记录状态的代码:

    public function onMessage(ConnectionInterface $connection, MessageInterface $message)
    {
        var_dump(json_decode($message->getPayload()));
		// 每当收到消息时,设置当前连接状态为“在线”,60 秒后过期
        \Cache::put($connection->socketId, '在线', 60);
        $message = PusherMessageFactory::createForMessage($message, $connection, $this->channelManager);

        $message->respond();
    }

	public function onClose(ConnectionInterface $connection)
    {
        $this->channelManager->removeFromAllChannels($connection);

        var_dump('close');
        // 浏览器主动断开连接时,设置当前连接状态为“离线”,不设置过期时间
        \Cache::put($connection->socketId, '离线');
    }

然后修改下app/resources/views/login.blade.php视图文件,在页面中显示连接的状态:

	<body>
        <input type="text" name="username">
        <input type="text" name="password">
        <button>登陆</button>
		<!-- 我们在这里简单的展示一下连接的状态 -->
        <h1>Websocket 连接列表</h1>
		<!-- 用 blade 模板语法渲染数据,简单的打印下数据 -->
        <pre>{{ var_export(\Cache::get('socekt-status')) }}</pre>
    </body>

大功告成,我们来看下效果。先打开http://<your.host>/login页面,此时未显示任何内容。这是正常的,因为Cache中还没有记录任何信息。接下来再打开http://<your.host>/hello页面,看下浏览器开发者工具中 websocket 已连接成功。下面见证奇迹的时刻到了,我们再将http://<your.host>/login页面刷新一次,此时会看到Cache中记录的在线状态信息:

「手把手」利用laravel-echo主动向服务端发送消息,实现在线状态管理

我们再来试下断开 websocekt 连接时,是否会显示离线。将http://<your.host>/login页面直接关闭,也就是点标签页上的「叉子」。再刷新下http://<your.host>/hello页面:

「手把手」利用laravel-echo主动向服务端发送消息,实现在线状态管理

本文尽量简化过程,减少无关操作,也将实现步骤尽可能详细的展现出来看,希望能对大家有所帮助。

posted @ 2021-03-28 22:04  LiamHao  阅读(686)  评论(0编辑  收藏  举报