当前位置:首页 > php > 正文内容

从0到1搭建基于swoole的websocket服务

phpmianshi3个月前 (11-16)php95

源码安装

wget  https://github.com/swoole/swoole-src/archive/v4.5.7.tar.gz
tar -xzvf v4.5.7.tar.gz
cd swoole-src-4.5.7 && \
phpize && \
./configure && \
make && sudo make install

启动扩展,需要在 php.ini 中加入如下配置来启用 Swoole 扩展

 extension=swoole.so


验证是否配置成功

php -m |grep swoole


如果没有可能是 php.ini 的路径不对。
可以使用 php --ini 来定位到 php.ini 的绝对路径,Loaded Configuration File 一项显示的是加载的 php.ini 文件,如果值为 none 证明根本没加载任何 php.ini 文件,需要自己创建。


websocket示例

server代码

<?php
/**
 * 第一版本正常运行的
 */
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9501);

//Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL);

$ws->set(array(
    'debug_mode' => 1,
    'worker_num' => 1,
    'max_request' => 10000,
    'daemonize' => 1, //以守护进程执行
    'dispatch_mode' => 2,
    'task_worker_num' => 2, //task进程的数量
    'max_wait_time' => 60,
    'reload_async' => true,
    'heartbeat_check_interval' => 60,
    'heartbeat_idle_time' => 120,
    'pid_file' => '/data/nginx/logs/swoole.pid',
    "log_file" => "/data/nginx/logs/swoole.log",//日志
    "log_rotation" => SWOOLE_LOG_ROTATION_DAILY,
    "open_eof_check" => true,//打开EOF检测
    "package_eof" => "\r\n", //设置EOF
    "open_cpu_affinity" => true,
    "stats_file" => "/data/nginx/logs/swoole.stat",
//    'ssl_cert_file' => __DIR__ . '/../config/fullchain.pem',
//    'ssl_key_file' => __DIR__ . '/../config/privkey.pem'
));

$newprocess = new Swoole\Process(function ($process) {
    global $ws;//同一个ws
    $redis = new Swoole\Coroutine\Redis();
    $redis->connect('127.0.0.1', 6379, true);
    $redis->auth("crs1rwoijdvl6rqd83dvN2jaF");
    if ($redis->subscribe(['msg'])) // 或者使用psubscribe
    {
        while ($msg = $redis->recv()) {
            // msg是一个数组, 包含以下信息
            // $type # 返回值的类型:显示订阅成功
            // $name # 订阅的频道名字 或 来源频道名字
            // $info  # 目前已订阅的频道数量 或 信息内容
            list($type, $name, $info) = $msg;
            if ($type == 'subscribe') { // 或psubscribe
                // 频道订阅成功消息,订阅几个频道就有几条
            } else if ($type == 'unsubscribe' && $info == 0) { // 或punsubscribe
                break; // 收到取消订阅消息,并且剩余订阅的频道数为0,不再接收,结束循环
            } else if ($type == 'message') {  // 若为psubscribe,此处为pmessage
//                    var_dump($name); // 打印来源频道名字
                var_dump("pid=" . getmypid() . " msg=" . $info); // 打印消息
                var_dump(count($ws->connections) . " conns");
//                    $conn = $ws->getClientList();
//                    var_dump($conn);
                foreach ($ws->connections as $fd) {
                    var_dump($fd . " fd");
                    // 需要先判断是否是正确的websocket连接,否则有可能会push失败
                    if ($ws->isEstablished($fd)) {
                        //向 WebSocket 客户端连接推送数据,长度最大不得超过 2M。
                        $ws->push($fd, "hello, welcome $info\n");
                    } else {
                        var_dump($fd . " fd error");
                    }
                }
                // balabalaba.... // 处理消息
//                    if ($need_unsubscribe) { // 某个情况下需要退订
//                        $redis->unsubscribe(); // 继续recv等待退订完成
//                    }
            }
        }
    }
}, false, 2, 1);
$ws->addProcess($newprocess);

//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
//    $ws->connections[] = $request->fd;
    var_dump($request->fd);
    $ws->push($request->fd, "open\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";
});


//workstart事件
$ws->on('workerstart', function ($ws, $work_id) {

});


//server task
$ws->on('task', function ($ws, $taskid, $from_id, $data) {
    global $table;
    foreach ($table as $row) {
        $ws->send($row['fd'], "{$row['fd']} i am broadcast");
    }

    $ws->finish("$data -> OK");
});

//处理异步任务的结果
$ws->on('finish', function ($ws, $task_id, $data) {
    echo "AsyncTask[$task_id] Finish: $data" . PHP_EOL;
});

$ws->start();

client.html

<script>
    var wsServer = 'ws://123.207.190.86:9501';
    var websocket = new WebSocket(wsServer);
    websocket.onopen = function (evt) {
        console.log("Connected to WebSocket server.");
        setInterval(ping, 60*1000);
    };

    websocket.onclose = function (evt) {
        console.log("Disconnected");
    };

    websocket.onmessage = function (evt) {
        console.log('Retrieved data from server: ' + evt.data);
    };

    websocket.onerror = function (evt, e) {
        console.log('Error occured: ' + evt.data);
    };

    function ping(){
        console.log('ping');
        websocket.send("ping");
    }
</script>


client.php


<?php

Co\run(function () {
    $client = new Swoole\Coroutine\Http\Client("123.207.190.86", 9501);
    $ret = $client->upgrade("/");
    if ($ret) {
        while (true) {
            $client->push("hello");
            var_dump($client->recv());
            co::sleep(1);
        }
    }
});


lumen+swoole

安装 laravelS https://github.com/hhxsv5/laravel-s/blob/master/README-CN.md#%E7%89%B9%E6%80%A7

composer require "hhxsv5/laravel-s:~3.7.8" -vvv


参考

https://wiki.swoole.com/#/environment

版权声明:本文由PHP面试资料网发布,如需转载请注明出处。
分享给朋友:

相关文章

PHP程序员解决问题能力的六个级别

青铜  var_dump/die打印变量值信息单步调试是最简单粗暴有效的解决问题方法。高级一点的是使用打印日志。白银 会设置各种错误日志的记录和显示,并根据各种错误日志分析错误或者搜索别人的...

PHP内核分析之生命周期(三)

一、概览PHP生命周期有五个阶段,分别为模块初始化阶段、请求初始化阶段、执行阶段、请求关闭阶段、模块关闭阶段。只是不同SAPI模式下,请求的情况略有不同,比如FastCGI下只经历了一次模块初始化阶段...

laravel中设置数据库读写分离后强制使用主库查询

背景在项目比较火爆,QPS比较高时,可以设置读写分离来扩容数据库,减少数据库的压力,但是有些业务可能对数据一致性要求比较高,比如支付。当支付成功时,再去查询订单状态时,如果查询走的从库,如果出现主从延...

PHP红包分配算法

<?php /**  * User: phpmianshi.com   * Date: 2020/3/31 &nbs...

PHP7 数组的底层实现

PHP7 数组的底层实现

PHP 数组具有的特性PHP 的数组是一种非常强大灵活的数据类型,在讲它的底层实现之前,先看一下 PHP 的数组都具有哪些特性。可以使用数字或字符串作为数组健值$arr = [1&...

PHP中跨域原理以及解决方案

1.为啥出现跨域在制定Html规则时,为了安全的考虑,一个源的脚本(网页,网站)不能与另一个源的资源进行交互,所以就引发一个词叫做“同源策略”。所谓同源(即指在同一个域),就是两个页面具有相同的协议(...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。