## start_io.php 完整代码,含二开调试内容,辅助用户深入理解交互代码逻辑 🙅 <div style="width:100%;padding:20px; height: 360px; background-color: #ffffff; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);"> <h2 >讲解</h2> 🐭、启动一个端口为2120的socket的服务`$sender_io = new SocketIO(2120);` 🐮、客户端连接以后所有的回调事件都在这个里面处理`$sender_io->on('connection', function($socket){` 🐅、`$socket->on('login'` 是本文档里面定义的login回调事件用于绑定用户id进行登录注册并且回调 🐰、`$socket->on('send_message'` 是响应前端的`send_message`事件发送过来的消息,处理的结果发送给前端代码中绑定的`new_msg`事件。 🐉、切记一点就是每一个监听的on的回调,前后端都是对应的。不可能前端有后端没有,也不可能后端有前端没有。成对出现的,相当于一个锚点。 🐍、前后端都无需单独配置心跳,socket.io自带心跳 </div> ***** ``` <?php use Workerman\Worker; use Workerman\Timer; use PHPSocketIO\SocketIO; use Workerman\Protocols\Http\Request; use Workerman\Connection\TcpConnection; include __DIR__ . '/vendor/autoload.php'; // 全局数组保存uid在线数据 $uidConnectionMap = array(); // 记录最后一次广播的在线用户数 $last_online_count = 0; // 记录最后一次广播的在线页面数 $last_online_page_count = 0; // PHPSocketIO服务 $sender_io = new SocketIO(2120); // 客户端发起连接事件时,设置连接socket的各种事件回调 $sender_io->on('connection', function($socket){ // 当客户端发来登录事件时触发 $socket->on('login', function ($uid)use($socket){ global $uidConnectionMap, $last_online_count, $last_online_page_count; // 已经登录过了 if(isset($socket->uid)){ return; } // 更新对应uid的在线数据 $uid = (string)$uid; if(!isset($uidConnectionMap[$uid])) { $uidConnectionMap[$uid] = 0; } // 这个uid有++$uidConnectionMap[$uid]个socket连接 ++$uidConnectionMap[$uid]; // 将这个连接加入到uid分组,方便针对uid推送数据 $socket->join($uid); $socket->uid = $uid; // 更新这个socket对应页面的在线数据 $socket->emit('update_online_count', "当前<b>{$last_online_count}</b>人在线,共打开<b>{$last_online_page_count}</b>个页面"); $socket->emit('new_msg', '链接成功了,开心吧'); }); //新增前端消息接收回调函数 $socket->on('send_message', function ($data)use($socket){ global $uidConnectionMap, $last_online_count, $last_online_page_count; // 已经登录过了 $data=json_decode($data,true); $to = $data['to']; $from = $socket->uid; $msg=$data['msg'].'-消息来自:'.$from; // $socket->emit('new_msg', '我接收到了你的消息:'.$msg); $socket->to($to)->emit('new_msg', '我接收到了你的消息:'.$msg); }); // 当客户端断开连接是触发(一般是关闭网页或者跳转刷新导致) $socket->on('disconnect', function () use($socket) { if(!isset($socket->uid)) { return; } global $uidConnectionMap, $sender_io; // 将uid的在线socket数减一 if(--$uidConnectionMap[$socket->uid] <= 0) { unset($uidConnectionMap[$socket->uid]); } }); }); // 当$sender_io启动后监听一个http端口,通过这个端口可以给任意uid或者所有uid推送数据 $sender_io->on('workerStart', function(){ // 监听一个http端口 $inner_http_worker = new Worker('http://0.0.0.0:2121'); // 当http客户端发来数据时触发 $inner_http_worker->onMessage = function(TcpConnection $http_connection, Request $request){ global $uidConnectionMap; $post = $request->post(); $post = $post ? $post : $request->get(); // 推送数据的url格式 type=publish&to=uid&content=xxxx switch(@$post['type']){ case 'publish': global $sender_io; $to = @$post['to']; $post['content'] = htmlspecialchars(@$post['content']); // 有指定uid则向uid所在socket组发送数据 if($to){ $sender_io->to($to)->emit('new_msg', $post['content']); // 否则向所有uid推送数据 }else{ $sender_io->emit('new_msg', @$post['content']); } // http接口返回,如果用户离线socket返回fail if($to && !isset($uidConnectionMap[$to])){ return $http_connection->send('offline'); }else{ return $http_connection->send('ok'); } } return $http_connection->send('fail'); }; // 执行监听 $inner_http_worker->listen(); // 一个定时器,定时向所有uid推送当前uid在线数及在线页面数 Timer::add(1, function(){ global $uidConnectionMap, $sender_io, $last_online_count, $last_online_page_count; $online_count_now = count($uidConnectionMap); $online_page_count_now = array_sum($uidConnectionMap); // 只有在客户端在线数变化了才广播,减少不必要的客户端通讯 if($last_online_count != $online_count_now || $last_online_page_count != $online_page_count_now) { $sender_io->emit('update_online_count', "当前<b>{$online_count_now}</b>人在线,共打开<b>{$online_page_count_now}</b>个页面"); $last_online_count = $online_count_now; $last_online_page_count = $online_page_count_now; } }); }); if(!defined('GLOBAL_START')) { Worker::runAll(); } ```