一、安装workerman
命令运行:
composer require topthink/think-worker (或 composer require workerman/workerman)
二、配置config/worker_server.php文件里面的worker_class参数(注意:反斜杠不能写错),如下图所示:
三、在上图worker_class参数路径创建文件worker.php,如下图所示:
文件内容如下:
<?php namespace app\index\controller; use think\facade\Db; use think\worker\Server; use Workerman\Lib\Timer; //define('HEARTBEAT_TIME', 20); // 心跳间隔 class Worker extends Server { protected $socket = 'websocket://0.0.0.0:2345/wss'; //设置地址 protected $online_count = 0; //统计在线人数 /* 此处为实现SSL证书wss连接 protected $context = [ 'ssl' => [ 'local_cert' => 'C:/Apache24/ssl/api_hilo8_com.crt', //证书文件的存放路径(请改成自己的路径) 'local_pk' => 'C:/Apache24/ssl/api_hilo8_com.key', //证书私钥的存放路径(请改成自己的路径) 'verify_peer' => false, //'allow_self_signed' => true, //如果是自签名证书需要开启此选项 ], ]; protected $option = [ 'transport' => 'ssl', //设置transport开启ssl,启用wss:// ]; */ /** * 收到信息 * @param $connection * @param $data */ public function onMessage($connection, $data) { $origin = json_decode($data, true); switch ($origin['type']) { //绑定用户 case 'login': $connection->uid = $origin['uid']; $connection->uname = $origin['uname']; $connection->login_time = time(); $this->online_count++; $this->onlineUid(); $send_content = [ 'type' => 'text', 'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已上线!' . $connection->login_time ]; $this->sendMessage($send_content); break; //收到消息,并回复 case 'text': $user = []; $user['from_uid'] = $origin['from_uid']; // 发送方用户uid $user['from_uname'] = $origin['from_uname']; //发送方用户名称 $user['to_uid'] = $origin['to_uid']; // 接收方用户uid $user['to_uname'] = $origin['to_uname']; //接收方用户名称 $content = $origin['content']; // 需要发送到对方的内容 $send_content = [ 'type' => 'text', 'user' => $user, 'content' => $content ]; $this->sendMessage($send_content, $origin['to_uid']); break; } echo ("收到消息:{$data}\n"); } /** * 当连接建立时触发的回调函数 * @param $connection */ public function onConnect($connection) { $link_time = time(); echo "连接时间:{$link_time}\n"; $send_content = [ 'type' => 'connect', 'content' => '连接成功:' . $link_time ]; $connection->send(json_encode($send_content, JSON_UNESCAPED_UNICODE)); } /** * 当连接断开时触发的回调函数 * @param $connection */ public function onClose($connection) { if (isset($connection->uid)) { echo "连接关闭 " . $connection->uid . "=" . $connection->uname . " (time:" . time() . ")\n"; $this->online_count--; $send_content = [ 'type' => 'text', 'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已下线!' . time() ]; $this->sendMessage($send_content); } } /** * 当客户端的连接上发生错误时触发 * @param $connection * @param $code * @param $msg */ public function onError($connection, $code, $msg) { echo "error $code $msg\n"; } /** * 每个进程启动 * @param $worker */ public function onWorkerStart($worker) { Timer::add(10, function () use ($worker) { if ($this->online_count > 0) { $this->onlineUid(); } }); } //统计在线用户 private function onlineUid() { $online_uid = $online_data = []; foreach ($this->worker->connections as $connection) { if (isset($connection->uid)) { //array_push($online_uid, $connection->uid . '=' . $connection->uname); $online_data[] = ['uid' => $connection->uid, 'uname' => $connection->uname]; } } $send_content = [ 'type' => 'online', 'count' => $this->online_count, 'content' => $online_data ]; $this->sendMessage($send_content); } //发送消息 private function sendMessage($content = [], $uid = 0) { $message = json_encode($content, JSON_UNESCAPED_UNICODE); if (is_numeric($uid) && $uid > 0) { //单发消息 foreach ($this->worker->connections as $connection) { if (isset($connection->uid) && $connection->uid == $uid) { $connection->send($message); } } } else { //群发消息 foreach ($this->worker->connections as $connection) { $connection->send($message); } } } }
说明:虽然上面可以实现SSL证书wss连接,但不建议这样做,建议以服务器代理方式实现。
四、启动WebSocket,命令:php think worker:server
五、客户端连接,代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebSocket聊天</title> <script type="text/javascript" src="https://www.hilo8.com/js/jquery.js"></script> <style> html, body { height: 100%; margin: 0; padding: 0; } .container { position: relative; height: 100%; overflow: auto; } .online-box { position: absolute; width: 150px; height: 100%; background-color: antiquewhite; } .online-box h4 { text-align: center; } .online-box ul li { cursor: pointer; padding: 5px 0px; font-size: 14px; } .online-box ul li:hover { color: #c00; } .main-box { margin-left: 150px; } #msg { color: #999; font-size: 12px; background-color: #f6f6f6; padding: 10px; } #msg p { margin: 10px 0px 0px 0px; } #msg p span { border: 1px solid #ddd; border-radius: 4px; padding: 3px 5px; display: block; font-size: 14px; color: #333; background-color: #fff; float: left; } #msg p span.self { background-color: #090; color: #fff; float: right; } #msg font{font-size: 12px; color: #999; display: block;} .clear { clear: both; *zoom: 1 } .clear:after { content: '\20'; clear: both; *zoom: 1; display: block; height: 0 } </style> </head> <body> <div class="container"> <div class="online-box"> <h4>在线用户列表</h4> <ul> <li data-user="0,所有人">所有人</li> </ul> </div> <div class="main-box"> <h1>WebSocket示例</h1> <p> <input type="text" id="uname" placeholder="请输入您的名称" /> <button type="button">连接</button> <button type="button">关闭</button> </p> <p> 接收人: <span id="to-uname-span">所有人</span><br> <input type="hidden" id="to-uid" value="0"><input type="hidden" id="to-uname" value="所有人"> <input type="text" id="content" placeholder="请输入要发送的内容" /> <button>发送</button> </p> <hr> <div id="msg" class="clear"></div> </div> </div> <script> $(function(){ var socket; var uid = Math.floor(Math.random() * 10000) + 1; //随机生成用户id号 var uname = ''; //用户名称 /* 连接状态 */ var state = 0; var sockState = function () { let status = ['未连接', '连接成功,可通信', '正在关闭', '连接已关闭或无法打开']; return status[state]; } //显示服务器返回消息 var showMsg = function (message) { $("#msg").append('<p class="clear">' + message + '</p>'); } /* 打开连接事件 */ $("button:eq(0)").click(function () { uname = $('#uname').val(); if (uname == '') { alert('请输入您的名称再连接'); return; } try { /* 连接 */ socket = new WebSocket("ws://api.shiyunkj.com:2345/wss"); /* 绑定事件 */ /* 监听连接状态 */ socket.onopen = function () { state = socket.readyState; socket.send(JSON.stringify({ type: 'login', uid: uid, uname: uname })); }; /* 接收消息 */ socket.onmessage = function (e) { let res = JSON.parse(e.data); switch (res.type) { case 'online': let online_count = $('.online-box ul li').length - 1; let lis = '<li data-user="0,所有人">所有人</li>'; if (online_count != res.count) { $.each(res.content, function (key, val) { lis += '<li data-user="' + val.uid + ',' + val.uname + '">' + val.uname + '</li>'; }) $('.online-box ul').html(lis); } break; case 'connect': case 'text': let str = ''; if (res.user != undefined) { let user = res.user.from_uname || ''; if (user != '') { str = '<font>'+user+'</font>'; } } showMsg(str+"<span>" + res.content + '</span>'); break; default: showMsg("接收信息出错..."); } //showMsg("<br>" + res.content); }; /* 关闭连接 */ socket.onclose = function () { state = socket.readyState; showMsg("连接已关闭或无法连接服务器..." + state); }; } catch (exception) { showMsg("有错误发生..."); } }); /* 发送数据事件 */ $("button:eq(2)").click(function () { if (state != 1) { alert(sockState()); return; } else if ($("#content").val() == "") { alert("请输入发送内容!"); return; } try { showMsg('<span class="self">' + $("#content").val() + '</span>'); let data = { type: "text", from_uid: uid, from_uname: uname, to_uid: $("#to-uid").val(), to_uname: $("#to-uname").val(), content: $("#content").val() }; socket.send(JSON.stringify(data)); } catch (exception) { showMsg("发送数据出错..."); } /* 清空文本框 */ $("#content").val(""); }); /* 断开连接 */ $("button:eq(1)").click(function () { try { showMsg("关闭连接..."); socket.close(); } catch (exception) { alert(sockState()); } }); //选择用户 $('.online-box ul').on('click', 'li', function () { let arr = $(this).data('user').split(','); $('#to-uid').val(arr[0]); $('#to-uname').val(arr[1]); $('#to-uname-span').html(arr[1]); }); }); </script> </body> </html>
可以多人在线聊天,如下图演示:
上一篇:TP6 生成Qrcode二维码和Barcode条形码的方法
讨论数量:0