laravel+swoole实现websocket

东门晓博
2023-12-01

1、创建WsSwoole.php文件(大驼峰命名,自命名)
在项目根目录执行以下命令

#在app/Console/Commands增加一个Swoole.php的文件
php artisan make:command WsSwoole

2、在Kernel.php里增加命令列表(如果定时任务必须加,这个可加可不加,建议加)

Commands\Swoole::Class

<?php

namespace App\Console;

use App\Console\Commands\AccessTokenRevoke;
use App\Console\Commands\UploadPart;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        WsSwoole::Class,//这个可以不加
        UploadPart::class
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('accessToken:revoke')->everyFiveMinutes(); //每五分钟调度一次token废弃任务
        $schedule->command('pst:uploadpart')->dailyAt('3:00');     //每天3点删除上传产生的碎片

    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

3、编辑WsSwoole文件

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class WsSwoole extends Command
{
    public $ws;
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'swoole {action?}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'swoole';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $action = $this->argument('action');
        switch ($action) {
            case 'restart':
                $this->info('swoole server restarted');
                break;
            case 'close':
                $this->info('swoole server stoped');
                break;
            default:
                $this->start();
                break;
        }
    }

    public function start()
    {
        //创建websocket服务器对象,监听0.0.0.0:9501端口
        $this->ws = new \swoole_websocket_server("0.0.0.0", 9501);
        //上面端口可以写到配置文件中 配置文件大概样式在最后
//$this->ws =new \swoole_websocket_server(config('web_socket.host'),config('web_socket.port'), SWOOLE_PROCESS, SWOOLE_SOCK_TCP );
        //开启ssl模式
//        $this->ws = new \swoole_websocket_server("0.0.0.0", 9501,SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);

        //配置ssl模式
//        $this->ws->set([
//            'ssl_key_file' => '/var/www/html/ssl/4033464_cxh520.top.key',
//            'ssl_cert_file' => '/var/www/html/ssl/4033464_cxh520.top.pem'
//        ]);

        //监听WebSocket连接打开事件
        $this->ws->on('open',[$this,'open']);

        //监听WebSocket消息事件
        $this->ws->on('message',[$this,'message']);

        //监听WebSocket主动推送消息事件
        $this->ws->on('request',[$this,'request']);

        //监听WebSocket连接关闭事件
        $this->ws->on('close',[$this,'close']);

        $this->ws->start();
    }

    /**
     * 建立连接
     * @param $ws
     * @param $request
     */
    public function open($ws, $request){
        var_dump($request->fd . "连接成功");
    }

    /**
     * 接收消息
     * @param $ws
     * @param $frame
     */
    public function message($ws,$frame){
        var_dump('发送成功');
    }

    /**
     * 接收请求
     * @param $request
     * @param $response
     */
    public function request($request,$response){

    }

    /**
     * 关闭连接
     * @param $ws
     * @param $fd
     */
    public function close($ws,$fd){

    }
}


优化

        $tool=WebSocketTool::getWebSocketTool();//添加回调类 把open,message,request,close等放进WebSocketTool中,这样封装看起来比较整洁
        $this->ws->on('open',array($tool,'onOpen'));
        $this->ws->on('message',array($tool,'onMessage'));
        $this->ws->on('close',array($tool,'onClose'));
        $this->serv->start();

WebSocketTool部分代码大概这样

<?php
namespace App\Tools;

use App\Models\Pst;
use App\Models\Role;
use App\Models\User;
use App\WebSocket\WebSocketClient;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Redis;

/**
 * 短信工具类
 */
class WebSocketTool
{
    static private $webSocketTool;
    private $table;
    private $config;
    private $userTool;//用户工具类
    private $notifyTool;//通知工具类
    private $redis_u_info;//redis中存放user信息的hash表明
    private $redis_uid_fd;//user_id 关联fd hash表名

    /**
     *私有构造函数防止new
     */
    private function __construct()
    {
        $this->config = config('web_socket');
        $this->userTool = UserTool::getUserTool();
        $this->notifyTool = NotifyTool::getNotifyTool();
        $this->redis_u_info = $this->config['redis']['u_info'];
        $this->redis_uid_fd = $this->config['redis']['uid_fd'];
    }

    /**
     * 单例模式
     */
    static public function getWebSocketTool()
    {
        if (self::$webSocketTool instanceof self) {
            return self::$webSocketTool;
        } else {
            return self::$webSocketTool = new self;
        }
    }

    /**
     * 防止被克隆
     */
    private function _clone()
    {

    }

    /**
     * 监听用户连接事件
     * @param \swoole_websocket_server $server
     * @param \swoole_http_request $request
     */
    public function onOpen(\swoole_websocket_server $server, \swoole_http_request $request)
    {
        if (!array_key_exists('client_type', $request->header)) {//判断是否从业务逻辑推送
            $user = User::find(FunctionTool::decrypt_id(($request->get)['user_id']));
            $r_data = [];//需要压入or取出的redis的数据
            //放入redis缓存中
            $fd = $request->fd;
            $r_data = [
                'fd' => $fd,
                'user_id' => $user->id,
                'name' => $user->name,
            ];
            WsSendMessageTool::bindFdUser($this->redis_u_info, $this->redis_uid_fd, $r_data, $fd, $user->id);//绑定用户信息
            dump('ws服务器用户绑定');
            dump(Redis::hgetall($this->redis_u_info), Redis::hgetall($this->redis_uid_fd));

        }
    }

    /**
     * 监听用户连接关闭状态
     * @param \swoole_websocket_server $server
     * @param int $fd
     */
    public function onClose(\swoole_websocket_server $server, int $fd)
    {
        dump('连接断开逻辑');
        WsSendMessageTool::removeFdUser($this->redis_u_info, $this->redis_uid_fd, $fd);
    }

    /**
     * 监听客户端向服务端推送数据
     * @param \swoole_websocket_server $server
     * @param \swoole_websocket_frame $frame
     */
    public function onMessage(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
    {
    

    }
}

4、前端代码

const ws = new WebSocket('ws://服务器ip:9501');
ws.onopen = () =>{
	console.log("连接成功");
          ws.send('111');
}

ws.onmessage = ({data}) =>{
	
}

ws.onclose = () =>{

}

5、主动推送代码(放在任何控制器位置调用即可)

public function swooleCurl($data)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, "http://服务器ip:9501");
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_HEADER, 1);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    curl_exec($curl);
    curl_close($curl);
}

6、服务器项目根目录运行swoole,下面两条命令都可以

#启动
php artisan swoole start
#启动并守护
php artisan swoole &

启动命令
与WsSwoole文件相关
protected $signature = ‘swoole {action?}’;
如果改为 protected $signature = ‘swoole:action {action}’;//问号可以不用
则启动命令为 php artisan swoole:action start

封装优化
在WsSwoole中start()的优化配置文件

<?php

return [
    /*
     * swoole服务器启动的地址&端口
     */
    'host' => '0.0.0.0',
    'port' => 9501,
    /*
     *swoole演示聊天室启动的地址&端口
     */
    'chat_host'=>'0.0.0.0',
    'chat_port'=>9502,
    'redis' => [
        'u_info'=>'u_info',//存放用户通道绑定信息的hash表名
        'uid_fd'=>'uid_fd',//通道id与user_id关联表名
    ],
];
 类似资料: