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);

推荐阅读更多精彩内容