内容简介:PHPSocket.IO是PHP版本的Socket.IO服务端实现,基于workerman开发,用于替换node.js版本Socket.IO服务端。PHPSocket.IO底层采用websocket协议通讯,如果客户端不支持websocket协议, 则会自动采用http长轮询的方式通讯。创建文件命令
PHPSocket.IO ,PHP跨平台实时通讯框架
PHPSocket.IO是 PHP 版本的Socket.IO服务端实现,基于workerman开发,用于替换node.js版本Socket.IO服务端。PHPSocket.IO底层采用websocket协议通讯,如果客户端不支持websocket协议, 则会自动采用http长轮询的方式通讯。
环境
- Ubuntu 18
- Laravel 5.8
- PHPSocket.IO 1.1
安装依赖
composer require workerman/phpsocket.io composer require guzzlehttp/guzzle
启动程序整合到artisan命令中
创建文件命令 php artisan make:command MsgPush
app/Console/Commands/MsgPush.php
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Workerman\Worker; use Workerman\Lib\Timer; use PHPSocketIO\SocketIO; class MsgPush extends Command { protected $signature = 'msg-push {action=start : start | restart | reload(平滑重启) | stop | status | connetions} {--d : deamon or debug}'; protected $description = 'web消息推送服务'; // 全局数组保存uid在线数据 private static $uidConnectionCounter = []; // 广播的在线用户数,一个uid代表一个用户 private static $onlineCount = 0; // 广播的在线页面数,同一个uid可能开启多个页面 private static $onlinePageCount = 0; //PHPSocketIO服务 private static $senderIo = null; public function __construct() { parent::__construct(); } /** * 根据脚本参数开启PHPSocketIO服务 * PHPSocketIO服务的端口是`2120` * 传递数据的端口是`2121` */ public function handle() { global $argv; //启动php脚本所需的命令行参数 $argv[0] = 'MsgPush'; $argv[1] = $this->argument('action'); // start | restart | reload(平滑重启) | stop | status | connetions $argv[2] = $this->option('d') ? '-d' : ''; // 守护进程模式或调试模式启动 // PHPSocketIO服务 self::$senderIo = new SocketIO(2120); // 客户端发起连接事件时,设置连接socket的各种事件回调 self::$senderIo->on('connection', function ($socket) { // 当客户端发来登录事件时触发,$uid目前由页面传值决定,当然也可以根据业务需要由服务端来决定 $socket->on('login', function ($uid) use ($socket) { // 已经登录过了 if (isset($socket->uid)) return; // 更新对应uid的在线数据 $uid = (string)$uid; // 这个uid有self::$uidConnectionCounter[$uid]个socket连接 self::$uidConnectionCounter[$uid] = isset(self::$uidConnectionCounter[$uid]) ? self::$uidConnectionCounter[$uid] + 1 : 1; // 将这个连接加入到uid分组,方便针对uid推送数据 $socket->join($uid); $socket->uid = $uid; // 更新这个socket对应页面的在线数据 self::emitOnlineCount(); }); // 当客户端断开连接是触发(一般是关闭网页或者跳转刷新导致) $socket->on('disconnect', function () use ($socket) { if (!isset($socket->uid)) { return; } // 将uid的在线socket数减一 if (--self::$uidConnectionCounter[$socket->uid] <= 0) { unset(self::$uidConnectionCounter[$socket->uid]); } }); }); // 当self::$senderIo启动后监听一个http端口,通过这个端口可以给任意uid或者所有uid推送数据 self::$senderIo->on('workerStart', function () { // 监听一个http端口 $innerHttpWorker = new Worker('http://0.0.0.0:2121'); // 当http客户端发来数据时触发 $innerHttpWorker->onMessage = function ($httpConnection, $data) { $type = $_REQUEST['type'] ?? ''; $content = htmlspecialchars($_REQUEST['content'] ?? ''); $to = (string)($_REQUEST['to'] ?? ''); // 推送数据的url格式 type=publish&to=uid&content=xxxx switch ($type) { case 'publish': // 有指定uid则向uid所在socket组发送数据 if ($to) { self::$senderIo->to($to)->emit('new_msg', $content); } else { // 否则向所有uid推送数据 self::$senderIo->emit('new_msg', $content); } // http接口返回,如果用户离线socket返回fail if ($to && !isset(self::$uidConnectionCounter[$to])) { return $httpConnection->send('offline'); } else { return $httpConnection->send('ok'); } } return $httpConnection->send('fail'); }; // 执行监听 $innerHttpWorker->listen(); // 一个定时器,定时向所有uid推送当前uid在线数及在线页面数 Timer::add(1, [self::class, 'emitOnlineCount']); }); // Worker::$daemonize = true; Worker::runAll(); } /** * 将在线数变化推送给所有登录端 * 须是public方法,可供其它类调用 */ public static function emitOnlineCount() { $newOnlineCount = count(self::$uidConnectionCounter); $newOnlinePageCount = array_sum(self::$uidConnectionCounter); // 只有在客户端在线数变化了才广播,减少不必要的客户端通讯 if ($newOnlineCount != self::$onlineCount || $newOnlinePageCount != self::$onlinePageCount) { // var_dump('emitOnlineCount: ', self::$uidConnectionCounter); //将在线数变化推送给所有登录端 self::$senderIo->emit( 'update_online_count', [ 'onlineCount' => $newOnlineCount, 'onlinePageCount' => $newOnlinePageCount ] ); self::$onlineCount = $newOnlineCount; self::$onlinePageCount = $newOnlinePageCount; } } }
启动PHPSocket.Io服务
#守护进程模式启动 php artisan msg-push start -d #调式模式启动 php artisan msg-push start
web页面
resources/views/socketio.blade.php
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>laravel整合phpSocketIo</title> </head> <body> <h1>laravel整合phpSocketIo</h1> <h2>实现 laravel 服务端推送消息到web端</h2> <h5>效果查看console</h5> <script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script> <script> document.addEventListener('DOMContentLoaded', () => { const uid = Date.now(), //这个识别id可以换成项目相应业务的id,同一个id可以多端登录,能同时收到消息 domain = document.domain, //当前打开页面的域名或ip sendToOneApi = `http://${domain}:2121/?type=publish&content=msg_content&to=${uid}`, sendToAllApi = `http://${domain}:2121/?type=publish&content=msg_content`, socket = io(`http://${domain}:2120`); // 连接socket服务端 console.log('给指定uid登录端发送消息接口: ', sendToOneApi); //支持get和post方法 console.log('给所有登录端发送消息接口: ', sendToAllApi); // 连接后登录 socket.on('connect', function () { socket.emit('login', uid); }); // 后端推送来消息时 socket.on('new_msg', function (msg) { console.log('收到消息: ' + msg); }); // 后端推送来在线数据时 socket.on('update_online_count', function (online_stat) { console.log('即时在线数据: ', online_stat); }); }); </script> </body> </html>
web页面路由
routes/web.php
Route::get('/socketio', function () { return view('socketio'); });
laravel内以触发事件方式推送消息
app/Providers/EventServiceProvider.php
//定义事件 //App/Providers/EventServiceProvider public function boot() { parent::boot(); //推送消息到web端,这个闭包只能传入一个参数 Event::listen('send-msg', function (object $data) { // dump($data); $response = (new \GuzzleHttp\Client())->post('http://127.0.0.1:2121', [ 'form_params' => [ 'content' => $data->content, 'to' => $data->to ?? '', 'type' => $data->type ?? 'publish', ], ]); return (string)$response->getBody(); }); }
浏览器方式测试推送
地址栏输入 http://${domain}:2121/?type=publish&content=Are_you_ok
推送给全体成员, ${domain}
是你实际的ip或域名
tinker方式测试推送
#进入tinker php artisan tinker #推送给全体 event('send-msg',(object)['content'=>'hello']) #推送给个体,`to`改成你的实际值 event('send-msg',(object)['content'=>'hello','to'=>1556645595484])
通过以上操作即可在php服务端向web端推送消息啦,解锁新功能是不是有点小兴奋呢?
感谢推动着时代进步的巨人们,是你们让我等看到了更多的可能!
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- iOS 推送通知及推送扩展
- 安卓统一推送联盟明日开启推送通道测试
- 《Web 推送通知》系列翻译 | 第五篇:使用 Web 推送库发送消息 && 第六篇:Web 推送协议
- 推送系统从0到1(七):推送用户画像建立
- 推送系统从0到1(八):个性化精准推送的实现
- 重构推送服务
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。