laravel+swoole之websocket消息推送

1.首先安装swoole扩展

Swoole-1.x需要 PHP-5.3.10 或更高版本
Swoole-2.x需要 PHP-7.0.0 或更高版本

公司环境是php5.6.31所以比较麻烦需要编译安装,7以上直接使用命令
(pecl install swoole)

wget https://github.com/swoole/swoole-src/archive/v1.10.1.tar.gz
tar -zxvf v1.10.1.tar.gz
cd swoole-src-1.10.1
phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

然后在php.ini添加swoole.so扩展即可

2.使用laravel的artisan创建命令

php artisan make:command Swoole   #创建一个命令swoole并会在app/Console/Commands增加一个Swoole.php的文件

Commands\Swoole::Class  #在Kernel.php里增加命令列表

3.运行socket服务

1.编辑app/Console/Command里的Swoole.php文件

<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

class Swoole 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 'close':

                break;

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

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

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

        //监听WebSocket消息事件
        $this->ws->on('message', function ($ws, $frame) {
            // echo "Message: {$frame->data}\n";
            // $ws->push($frame->fd, "server: {$frame->data}");
            // var_dump($ws->connection_info($frame->fd));
            //fd绑定客户端传过来的标识uid
            $ws->bind($frame->fd, $frame->data);
        });

        $this->ws->on('request', function ($request, $response) {
                // 接收http请求从post获取参数
                // 获取所有连接的客户端,验证uid给指定用户推送消息
                // token验证推送来源,避免恶意访问
                if ($request->post['token'] == ### ) {
                    $clients = $this->ws->getClientList();
                    $clientId = [];
                    foreach ($clients as $value) {
                        $clientInfo = $this->ws->connection_info($value);
                        if (array_key_exists('uid', $clientInfo) && $clientInfo['uid'] == $request->post['s_id']) {
                            $clientId[] = $value;
                        }
                    }
                    if (!empty($clientId)) {
                        foreach ($clientId as $v) {
                            $this->ws->push($v, $request->post['info']);
                        }
                    }
                }
            });

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

        $this->ws->start();
    }
}
【注】此处为了结合app上传数据时使用curl触发request回调通知web端的实例所以使用了httpserver的onrequest事件,如果以后有更好的办法去触发服务端实时主动推送。

2.编辑html

<div id="test">
    <a href="javascript:void(0)">运行websocket</a>
</div>

$('#test').click(function(){
            if("WebSocket" in window){
                console.log("您的浏览器支持websocket\n");
                var ws = new WebSocket("ws://66.66.66.66:9502");//创建websocket对象 
                ws.onopen = function(){
                    // ws.send("连接已建立\n");
                    ws.send($("#content").attr("js-sid"));
                    console.log("数据发送中");
                }

                ws.onmessage = function(evt){
                    var recv_msg = evt.data;
                    console.log("接受到的数据为:"+recv_msg);
                }

                ws.onerror = function(evt,e){
                    console.log("错误信息为"+e);
                }

                ws.onclose = function(){
                    console.log("连接已关闭");
                }

            }else{
                console.log("您的浏览器不支持websocket\n");
            }
        });

3.curl方法(调用就行)

public function swooletest($param = ['s_id'=>2, 'info'=>'info'])
    {
        $param['token'] = ###;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:9502");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        //设置post数据
        $post_data = $param;
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_exec($ch);
        curl_close($ch);
    }

4.测试的时候直接在laravel的artisan所在目录使用命令php artisan swoole即可启动socket服务,然后页面运行客户端,最后调用curl推送数据。

4.成功之后

用supervisor守护swoole命令,或者nohup后台启动。

supervisor配置麻烦不过可以自动重启,nohup一条命令解决

nohup php artisan swoole &    #一条命令解决

此处说明几个问题

1.此处我采用的是bind方法,当客户端连接的时候send一个uid过来,然后在服务端处理的时候把uid和fd绑定在一起,当你想向某个客户端发送数据时传一个uid,通过uid找到fd进行指定发送,但是此处我用的是遍历getClientList所有连接用户(方法欠佳)的信息connection_info进行判定。希望能改善这种方法

2.因为是curl访问httpserver的形式,所以为了避免恶意访问,加一个token验证。

3.推送的信息转换成json再传,即info值

4.本实例的账户可能会在多个终端登录,有多个fd绑定uid,所以遍历推送push

参考

http://www.cnblogs.com/dcb3688/p/4608056.html

https://laravel-china.org/articles/4633/with-supervisor-use-swoole-to-create-a-websocket-server-in-laravel

https://wiki.swoole.com/wiki/page/397.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容