laravel中使用Swoole进行前后端实时通信

使用artisan命令创建Swoole任务

php artisan make:command Swoole

在App\Console\Commands下生成Swoole.php文件

<?php
namespace App\Console\Commands;
use Exception;
use Swoole\Process;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class RealTimePush extends Command
{
    private $swoole;
    private $pid_file;

    protected $signature = 'realtimePush {action}';

    protected $description = '实时推送';

    public function __construct()
    {
        $this->pid_file = __DIR__ . '/../../../storage/logs/swoole_websocket_realtime.pid';
    }

    public function handle()
    {
        $action = $this->argument('action');
        switch ($action) {
            case 'stop':   //停止命令
                $this->stop();
                break;
            case 'start':  //启动命令
                $this->start();
                break;
            default:
                $this->error('没有' . $action . '这个命令');
                break;
        }
    }

    // 启动服务
    public function start()
    {
        $pid = $this->getPid();
        //检测服务是否启动
        if ($pid && Process::kill($pid, 0)) {
            $this->error('服务已启动');
            exit;
        }
        //创建websocket服务,监听ip:端口
        $this->swoole = new \swoole_websocket_server(192.168.0.1, 9500);
        $this->swoole->set([
            //60秒检测一次,一个连接如果300秒内未向服务器发送任何数据,此连接将被强制关闭
            'heartbeat_check_interval' => 60, 'heartbeat_idle_time' => 300,
            //进程id存储路径
            'pid_file' => $this->pid_file,
        ]);
        //监听WebSocket连接打开事件
        $this->swoole->on('open', [$this, 'open']);
        //监听WebSocket消息事件
        $this->swoole->on('message', [$this, 'message']);
        //监听WebSocket主动推送消息事件
        $this->swoole->on('request', [$this, 'request']);
        //监听WebSocket连接关闭事件
        $this->swoole->on('close', [$this, 'close']);
        //启动事件
        $this->swoole->start();
    }

    // 建立连接
    public function open($swoole, $request)
    {
        //$request->fd是连接当前页面的ID
        if (isset($request->get) && isset($request->get['token'])) {
                $swoole->push($request->fd, json_encode(['code' => 200, 'msg' =>'连接成功']));
            } else {
                 $swoole->push($request->fd, json_encode(['code' => 400, 'msg' =>'没有登录']));
                 //断开连接
                 $swoole->close($request->fd);
            }
    }

    //接收前端发送的消息
    public function message($swoole, $frame)
    {
        $swoole->push($frame->fd, '已收到发送的消息');
    }

    // 接收服务器请求
    public function request($request, $response)
    {
       try {
            //请求的数据
            $post = $request->post;
            //拼接redis的key
            $key = 'realTimePush' . date('Ymd');
            $redis = new \Redis();
            $redis->connect('127.0.0.1', 6379);
            $redis->auth('passworld');
            $redis->select(0);
            $datas = [];
            //获取所有连接fd
            $clients = $this->swoole->getClientList();
            //获取队列的长度
            $num = $redis->lLen($key);
            //连接fd是空 队列里没有推送内容
            if (!empty($clients) && $num > 0) {
                //循环取出数据
                while ($num > 0) {
                    $data = $redis->rpop($key);
                    if (!empty($data)) {
                        $datas[] = json_decode($data, true);
                    }
                    $num--;
                }
                //推送的数据
                $result = json_encode(['code' => 200, 'msg' => '成功', 'data' => $datas]);
                //循环推送数据到前端
                foreach ($clients as $fd) {
                    if ($this->swoole->isEstablished($fd)) {
                        if ($this->swoole->push($fd, $result)) {
                            //Log::info('推送成功');
                        }
                    }
                }
            }
            $redis->close();
            return true;
        } catch (Exception $e) {
            Log::error('推送:' . $e->getMessage());
            return false;
        }
    }

    // 关闭连接
    public function close($swoole, $fd)
    {
        $swoole->close($fd);
    }

    // 服务停止
    public function stop()
    {
        if (!$pid = $this->getPid()) {
            $this->error('没有服务id');
            exit;
        }
        //关闭服务
        if (Process::kill($pid)) {
            $this->info('服务已停止');
            exit;
        }
        $this->info('停止服务失败');
    }

    // 获取进程pid
    private function getPid()
    {
        return file_exists($this->pid_file) ? file_get_contents($this->pid_file) : false;
    }
}

启动/关闭Swoole服务命令

//启动服务命令
php artisan realtimePush start
//关闭服务命令
php artisan realtimePush stop

后端手动触发推送

    1. 后端启动服务
php artisan realtimePush start
    1. 前端通过websocket连接后端的9500端口
var ws = new WebSocket('192.168.0.1:9500');
ws.onopen = function(){
  console.log(“open”);
  ws.send(“hello”);
};
ws.onmessage = function(evt){
  console.log(evt.data)
};
ws.onclose = function(evt){
  console.log(“WebSocketClosed!”);
};
ws.onerror = function(evt){
  console.log(“WebSocketError!”);
};
    1. 前端联接之后,后端可以主动推送了
$data = [
     'code' => 200,
     'msg' => '成功',
     'data' => '推送内容'
];
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, '192.168.0.1:9500');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$content = curl_exec($curl);
curl_close($curl);
dump($content);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容