配合 Supervisor,在 Laravel 里使用 Swoole 创建一个 websocket 服务器

想实现一个wesocket的聊天室,看到网上说swoole对这方面的支持挺好,所以试一下把laravel与swoole结合一下,只是做了个尝试,可以运行成功,具体细节尚未完善。

1、swoole安装请参考官网文档

https://wiki.swoole.com/

2、参考最近手册在laravel里创建一个新的命令swoole

http://laravelacademy.org/post/6842.html
创建一个名叫swoole的命令 php artisan make:command Swoole
在Kernel.php里增加命令列表

protected $commands = [
        Commands\Swoole::Class
];

修改swoole文件如下:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class Swoole extends Command
{
    /**
     * 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 'close':

                break;

            default:
                $this->start();
                break;
        }

    }
    public function start()
    {
        //创建websocket服务器对象,监听0.0.0.0:9502端口
        $ws = new \swoole_websocket_server("0.0.0.0", 9502);

        //监听WebSocket连接打开事件
        $ws->on('open', function ($ws, $request) {
            // var_dump($request->fd, $request->get, $request->server);
            $ws->push($request->fd, "hello, welcome\n");
        });

        //监听WebSocket消息事件
        $ws->on('message', function ($ws, $frame) {
            echo "Message: {$frame->data}\n";
            $ws->push($frame->fd, "server: {$frame->data}");
        });

        //监听WebSocket连接关闭事件
        $ws->on('close', function ($ws, $fd) {
            echo "client-{$fd} is closed\n";
        });

        $ws->start();
    }
}

这个时候就可以在 php artisan list 里查到swoole命令了,具体看手册。

3、详细解读

a、start()函数里调用了swoole的swoole_websocket_server类创建一个websocket服务器,监听9502端口
b、几个事件请看swoole的官方手册,其实就是监听一个socket连接并响应消息。这是一个聊天室的雏形,可以在里使用laravel的任意功能来完成用户聊天室的定位,并给所有人发消息,形成实时聊天功能
c、比如用户登陆后发送消息时附带一个rid(聊天室)过来,程序把fd与rid对应,并给所有rid对应的fd广播消息

4、最好使用supervisor来守护swoole命令,使其在后台一直运行

我的配置如下:

[program:swoole-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/artisan swoole
autostart=true
autorestart=true
user=apache
numprocs=1 //注意这里一定只有一个,端口的问题~~
redirect_stderr=true
stdout_logfile=/path/worker.log

5、在html里实现聊天功能,只能自己跟自己聊,试试长连接,要结合程序请自己尝试~

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="msg"></div>
<input type="text" id="text">
<input type="submit" value="发送数据" onclick="song()">
<p>adfssa</p>
</body>
<script>
    document.querySelector("body").style.fontSize = '28px';
    var msg = document.getElementById("msg");
    var wsServer = 'ws://100.100.100.100:9502/'
    //调用websocket对象建立连接:
    //参数:ws/wss(加密)://ip:port (字符串)
    var websocket = new WebSocket(wsServer);
    //onopen监听连接打开
    websocket.onopen = function (evt) {
        //websocket.readyState 属性:
        /*
        CONNECTING    0    The connection is not yet open.
        OPEN    1    The connection is open and ready to communicate.
        CLOSING    2    The connection is in the process of closing.
        CLOSED    3    The connection is closed or couldn't be opened.
        */
        msg.innerHTML = websocket.readyState;
    };

    function song(){
        var text = document.getElementById('text').value;
        document.getElementById('text').value = '';
        //向服务器发送数据
        websocket.send(text);
    }
      //监听连接关闭
//    websocket.onclose = function (evt) {
//        console.log("Disconnected");
//    };

    //onmessage 监听服务器数据推送
    websocket.onmessage = function (evt) {
        msg.innerHTML += evt.data +'<br>';
//        console.log('Retrieved data from server: ' + evt.data);
    };
//监听连接错误信息
//    websocket.onerror = function (evt, e) {
//        console.log('Error occured: ' + evt.data);
//    };

</script>
</html>

谢谢各位大神们创造出来这么多好用的库、扩展

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
讨论数量: 10

觉得还是用单文件比较好,毕竟artisan启动的话,变量什么的还是不会主动释放,写业务容易造成内存泄露

6年前 评论

@reatang 这只是个尝试,主要是在laravel里业务处理起来方便,实际应用还要看需求的

6年前 评论

写的不错,正好需要一个思路。

6年前 评论

请问laravel+swoole如何解决热更新呢?

6年前 评论

@Tank 我对swoole研究并不多,只是试着玩了下,你可以看看swoole的文档

6年前 评论

@Tank emmmm...swoole结合laravel 代码热更新的问题,你解决了么?我最近也遇到这个问题,同样的..服务也是用artisan命令启动的

6年前 评论

swoole支持后台运行,只要改动一个配置就可以
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 8,
'daemonize' => false,// 设置后台运行
'max_request' => 10000,
'dispatch_mode' => 2,
'debug_mode'=> 1
));

6年前 评论

@reatang 单文件启动的话,是指在laravel之外直接用php启动一个单独的文件吗,那样岂不是和laravel相互独立了,在收到消息的回调函数中不能使用laravel中的各种功能了,比如orm、job等

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!