laravel8 swoole websocket

这几天写了个socket的通知消息,记录一下使用心得,网络上有很多资料,但特别乱。在开发过程中也遇到了各种各样的问题,特此记录一下。
仅在laravel8、swooletw/laravel-swoole: 2.8 中进行了测试,其他版本请自行测试

设置

swoole_http.php配置文件

websocket要设置task_enable_coroutine属性true,同时打开websocket的使用。
不开task_enable_coroutine会报错的,注意
在这里插入图片描述

swoole_websocket.php配置文件

需要自定义一下handler,parser,
不修改的话前端调用时一定要注意数据包的结构。

var data = {"test": "测试"};
websocket.send(encodeMessage('loginCheck', data));
function encodeMessage(event, data) 
{
	return JSON.stringify([event,data]);
}

自定义handler,parser的话可以任意定义

 // 'handler' => SwooleTW\Http\Websocket\SocketIO\WebsocketHandler::class,
    'handler' => \App\services\WebsocketHandler::class,
    // 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class,
    'parser' => \App\services\WebsocketParser::class,

WebsocketHandler.php

<?php

namespace App\services;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Swoole\Websocket\Frame;
use SwooleTW\Http\Server\Facades\Server;
use SwooleTW\Http\Websocket\HandlerContract;

class WebsocketHandler implements HandlerContract
{
    // 连接建立时触发
    public function onOpen($fd, Request $request)
   	{
        // 在触发 WebSocket 连接建立事件之前,Laravel 应用初始化的生命周期已经结束,你可以在这里获取 Laravel 请求和会话数据
        if (! $request->input('sid')) {
            $payload = json_encode(
                [
                    'sid' => uniqid(),
                ]
            );
             // 调用 push 方法向客户端推送数据,fd 是客户端连接标识字段
            App::make(Server::class)->push($fd, $payload);
            return true;
        }
        return false;
    }

    // 收到消息时触发
    public function onMessage(Frame $frame)
    {
    	//根据前端心跳的返回,可写自己的判断
        App::make(Server::class)->push($frame->fd, 'pong');
    }

    // 关闭连接时触发
    public function onClose($fd, $reactorId)
    {
        App::make(Server::class)->disconnect($fd);
    }
}

WebsocketParser.php

<?php

namespace App\services;

use SwooleTW\Http\Websocket\Parser;

//这里只是进行了接管,并没有拿它实现代码逻辑
class WebsocketParser extends Parser
{

    /**
     * Encode output payload for websocket push.
     * @param string $event
     * @param mixed $data
     * @return mixed
     */
    public function encode(string $event, $data)
    {
        $string = ['event' => $event, 'data' => $data];
        return json_encode($string);
    }

    /**
     * Input message on websocket connected.
     * Define and return event name and payload data here.
     * @param \Swoole\Websocket\Frame $frame
     * @return array
     */
    public function decode($frame)
    {
        //这里是解析客户端发来的数据,我们约定所有的传输都是json
        $json = $frame->data;
        $data = json_decode($json, true);
        if (!$data || !isset($data['event'])) {
            return ['event' => 'error', 'data' => $frame->data];
        }
        return ['event' => $data['event'], 'data' => $data['data'] ?? ''];
    }
}

重点是 websocket的 routes文件

在vendor/swooletw/laravel-swoole/routes文件夹中有一个websocket.php
具体如下

<?php


use Illuminate\Http\Request;
use SwooleTW\Http\Websocket\Facades\Websocket;

/*
|--------------------------------------------------------------------------
| Websocket Routes
|--------------------------------------------------------------------------
|
| Here is where you can register websocket events for your application.
|
*/

Websocket::on('connect', function ($websocket, Request $request) {
	echo "connect";
    // called while socket on connect
});


Websocket::on('disconnect', function ($websocket) {
	echo "disconnect";
    // called while socket on disconnect
});

Websocket::on('example', function ($websocket, $data) {
    $websocket->emit('message', $data);
});

这个文件定义了一些触发事件,你要在你的laravel的路由器中自己定义一下,进行接管。
可以非常方便像定义 laravel 路由一样,定义各种事件
在这里插入图片描述
这里我注释了 disconnect,这样就会直接进入我WebsocketHandler.php中的onClose,否则会先进入 disconnect 中进行处理。
当然你也可以定义其他事件,想怎么写都行。

前端

只是简单设置了心跳,其他的可以自己填写,注意前端调用时数据包的结构

<script>
   var url = 'ws://a.test1.com/ws';
    var ws;
    var lockReconnect = false; //避免ws重复连接
    
    createWebSocket(url);//连接服务器
    function createWebSocket(url) {
        try {
            if ('WebSocket' in window) {
                console.log("连接 WebSocket");
                ws = new WebSocket(url);
            } else if ('MozWebSocket' in window) {
                console.log("尝试重新连接 MozWebSocket");
                ws = new MozWebSocket(url);
            } else {
                alert("您的浏览器不支持websocket")
            }
            ws.onopen = function (event) {
                heartCheck.start(); //心跳检测重置
                console.log("已经与服务器建立了连接\r\n当前连接状态:" + this.readyState);
            };
 
            ws.onmessage = function (event) {
                heartCheck.reset().start(); //拿到任何消息都说明当前连接是正常的
                if (event.data !== 'pong') 
                {
                    data=JSON.parse(event.data);
                    console.log("接收到服务器发送的数据:\r\n" +event.data);
                }
            };
            ws.onclose = function (event) {
                console.log("已经与服务器断开连接\r\n当前连接状态:" + this.readyState);
                reconnect(url);
            };
            ws.onerror = function (event) {
                console.log("WebSocket异常!");
            };
        } catch (e) {
            console.log(e);
        }
 
    }
    function reconnect(url) {
        if (lockReconnect) return;
        lockReconnect = true;
        setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
            console.log("尝试重新连接");
            createWebSocket(url);//连接服务器
            lockReconnect = false;
        }, 1000);
    }
    //心跳检测
    var heartCheck = {
        timeout: 8000, //8s
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function () {
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function () {
            var self = this;
            this.timeoutObj = setTimeout(function () {
                //这里发送一个心跳,后端收到后,返回一个心跳消息,
                //onmessage拿到返回的心跳就说明连接正常
                ws.send("ping");
                console.log("ping!");
                self.serverTimeoutObj = setTimeout(function () { //如果超过一定时间还没重置,说明后端主动断开了
                    console.log("try=close");
                    ws.close();
                }, self.timeout)
            }, this.timeout)
        }
    };


</script>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值