原创TP6下workerman实现WebSocket及wss连接(安装SSL证书),并在线实现简单聊天实例

ThinkPHP 697 0 2024-07-08

TP6下workerman实现WebSocket及wss连接(安装SSL证书),并在线实现简单聊天实例

一、安装workerman

命令运行:

composer require topthink/think-worker (或 composer require workerman/workerman)

二、配置config/worker_server.php文件里面的worker_class参数(注意:反斜杠不能写错),如下图所示:

三、在上图worker_class参数路径创建文件worker.php,如下图所示:

文件内容如下:

LANG-PHP
www.hilo8.com
  1. <?php
  2. namespace app\index\controller;
  3. use think\facade\Db;
  4. use think\worker\Server;
  5. use Workerman\Lib\Timer;
  6. //define('HEARTBEAT_TIME', 20); // 心跳间隔
  7. class Worker extends Server
  8. {
  9. protected $socket = 'websocket://0.0.0.0:2345/wss'; //设置地址
  10. protected $online_count = 0; //统计在线人数
  11. /* 此处为实现SSL证书wss连接
  12. protected $context = [
  13. 'ssl' => [
  14. 'local_cert' => 'C:/Apache24/ssl/api_hilo8_com.crt', //证书文件的存放路径(请改成自己的路径)
  15. 'local_pk' => 'C:/Apache24/ssl/api_hilo8_com.key', //证书私钥的存放路径(请改成自己的路径)
  16. 'verify_peer' => false,
  17. //'allow_self_signed' => true, //如果是自签名证书需要开启此选项
  18. ],
  19. ];
  20. protected $option = [
  21. 'transport' => 'ssl', //设置transport开启ssl,启用wss://
  22. ];
  23. */
  24. /**
  25. * 收到信息
  26. * @param $connection
  27. * @param $data
  28. */
  29. public function onMessage($connection, $data)
  30. {
  31. $origin = json_decode($data, true);
  32. switch ($origin['type']) {
  33. //绑定用户
  34. case 'login':
  35. $connection->uid = $origin['uid'];
  36. $connection->uname = $origin['uname'];
  37. $connection->login_time = time();
  38. $this->online_count++;
  39. $this->onlineUid();
  40. $send_content = [
  41. 'type' => 'text',
  42. 'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已上线!' . $connection->login_time
  43. ];
  44. $this->sendMessage($send_content);
  45. break;
  46. //收到消息,并回复
  47. case 'text':
  48. $user = [];
  49. $user['from_uid'] = $origin['from_uid']; // 发送方用户uid
  50. $user['from_uname'] = $origin['from_uname']; //发送方用户名称
  51. $user['to_uid'] = $origin['to_uid']; // 接收方用户uid
  52. $user['to_uname'] = $origin['to_uname']; //接收方用户名称
  53. $content = $origin['content']; // 需要发送到对方的内容
  54. $send_content = [
  55. 'type' => 'text',
  56. 'user' => $user,
  57. 'content' => $content
  58. ];
  59. $this->sendMessage($send_content, $origin['to_uid']);
  60. break;
  61. }
  62. echo ("收到消息:{$data}\n");
  63. }
  64. /**
  65. * 当连接建立时触发的回调函数
  66. * @param $connection
  67. */
  68. public function onConnect($connection)
  69. {
  70. $link_time = time();
  71. echo "连接时间:{$link_time}\n";
  72. $send_content = [
  73. 'type' => 'connect',
  74. 'content' => '连接成功:' . $link_time
  75. ];
  76. $connection->send(json_encode($send_content, JSON_UNESCAPED_UNICODE));
  77. }
  78. /**
  79. * 当连接断开时触发的回调函数
  80. * @param $connection
  81. */
  82. public function onClose($connection)
  83. {
  84. if (isset($connection->uid)) {
  85. echo "连接关闭 " . $connection->uid . "=" . $connection->uname . " (time:" . time() . ")\n";
  86. $this->online_count--;
  87. $send_content = [
  88. 'type' => 'text',
  89. 'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已下线!' . time()
  90. ];
  91. $this->sendMessage($send_content);
  92. }
  93. }
  94. /**
  95. * 当客户端的连接上发生错误时触发
  96. * @param $connection
  97. * @param $code
  98. * @param $msg
  99. */
  100. public function onError($connection, $code, $msg)
  101. {
  102. echo "error $code $msg\n";
  103. }
  104. /**
  105. * 每个进程启动
  106. * @param $worker
  107. */
  108. public function onWorkerStart($worker)
  109. {
  110. Timer::add(10, function () use ($worker) {
  111. if ($this->online_count > 0) {
  112. $this->onlineUid();
  113. }
  114. });
  115. }
  116. //统计在线用户
  117. private function onlineUid()
  118. {
  119. $online_uid = $online_data = [];
  120. foreach ($this->worker->connections as $connection) {
  121. if (isset($connection->uid)) {
  122. //array_push($online_uid, $connection->uid . '=' . $connection->uname);
  123. $online_data[] = ['uid' => $connection->uid, 'uname' => $connection->uname];
  124. }
  125. }
  126. $send_content = [
  127. 'type' => 'online',
  128. 'count' => $this->online_count,
  129. 'content' => $online_data
  130. ];
  131. $this->sendMessage($send_content);
  132. }
  133. //发送消息
  134. private function sendMessage($content = [], $uid = 0)
  135. {
  136. $message = json_encode($content, JSON_UNESCAPED_UNICODE);
  137. if (is_numeric($uid) && $uid > 0) {
  138. //单发消息
  139. foreach ($this->worker->connections as $connection) {
  140. if (isset($connection->uid) && $connection->uid == $uid) {
  141. $connection->send($message);
  142. }
  143. }
  144. } else {
  145. //群发消息
  146. foreach ($this->worker->connections as $connection) {
  147. $connection->send($message);
  148. }
  149. }
  150. }
  151. }

说明:虽然上面可以实现SSL证书wss连接,但不建议这样做,建议以服务器代理方式实现。

四、启动WebSocket,命令:php think worker:server

五、客户端连接,代码如下:

LANG-JS
www.hilo8.com
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="renderer" content="webkit">
  6. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  7. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  8. <title>WebSocket聊天</title>
  9. <script type="text/javascript" src="https://www.hilo8.com/js/jquery.js"></script>
  10. <style>
  11. html,
  12. body {
  13. height: 100%;
  14. margin: 0;
  15. padding: 0;
  16. }
  17. .container {
  18. position: relative;
  19. height: 100%;
  20. overflow: auto;
  21. }
  22. .online-box {
  23. position: absolute;
  24. width: 150px;
  25. height: 100%;
  26. background-color: antiquewhite;
  27. }
  28. .online-box h4 {
  29. text-align: center;
  30. }
  31. .online-box ul li {
  32. cursor: pointer;
  33. padding: 5px 0px;
  34. font-size: 14px;
  35. }
  36. .online-box ul li:hover {
  37. color: #c00;
  38. }
  39. .main-box {
  40. margin-left: 150px;
  41. }
  42. #msg {
  43. color: #999;
  44. font-size: 12px;
  45. background-color: #f6f6f6;
  46. padding: 10px;
  47. }
  48. #msg p {
  49. margin: 10px 0px 0px 0px;
  50. }
  51. #msg p span {
  52. border: 1px solid #ddd;
  53. border-radius: 4px;
  54. padding: 3px 5px;
  55. display: block;
  56. font-size: 14px;
  57. color: #333;
  58. background-color: #fff;
  59. float: left;
  60. }
  61. #msg p span.self {
  62. background-color: #090;
  63. color: #fff;
  64. float: right;
  65. }
  66. #msg font{font-size: 12px; color: #999; display: block;}
  67. .clear {
  68. clear: both;
  69. *zoom: 1
  70. }
  71. .clear:after {
  72. content: '\20';
  73. clear: both;
  74. *zoom: 1;
  75. display: block;
  76. height: 0
  77. }
  78. </style>
  79. </head>
  80. <body>
  81. <div class="container">
  82. <div class="online-box">
  83. <h4>在线用户列表</h4>
  84. <ul>
  85. <li data-user="0,所有人">所有人</li>
  86. </ul>
  87. </div>
  88. <div class="main-box">
  89. <h1>WebSocket示例</h1>
  90. <p>
  91. <input type="text" id="uname" placeholder="请输入您的名称" />
  92. <button type="button">连接</button>
  93. <button type="button">关闭</button>
  94. </p>
  95. <p>
  96. 接收人: <span id="to-uname-span">所有人</span><br>
  97. <input type="hidden" id="to-uid" value="0"><input type="hidden" id="to-uname" value="所有人">
  98. <input type="text" id="content" placeholder="请输入要发送的内容" />
  99. <button>发送</button>
  100. </p>
  101. <hr>
  102. <div id="msg" class="clear"></div>
  103. </div>
  104. </div>
  105. <script>
  106. $(function(){
  107. var socket;
  108. var uid = Math.floor(Math.random() * 10000) + 1; //随机生成用户id号
  109. var uname = ''; //用户名称
  110. /* 连接状态 */
  111. var state = 0;
  112. var sockState = function () {
  113. let status = ['未连接', '连接成功,可通信', '正在关闭', '连接已关闭或无法打开'];
  114. return status[state];
  115. }
  116. //显示服务器返回消息
  117. var showMsg = function (message) {
  118. $("#msg").append('<p class="clear">' + message + '</p>');
  119. }
  120. /* 打开连接事件 */
  121. $("button:eq(0)").click(function () {
  122. uname = $('#uname').val();
  123. if (uname == '') {
  124. alert('请输入您的名称再连接');
  125. return;
  126. }
  127. try {
  128. /* 连接 */
  129. socket = new WebSocket("ws://api.shiyunkj.com:2345/wss");
  130. /* 绑定事件 */
  131. /* 监听连接状态 */
  132. socket.onopen = function () {
  133. state = socket.readyState;
  134. socket.send(JSON.stringify({ type: 'login', uid: uid, uname: uname }));
  135. };
  136. /* 接收消息 */
  137. socket.onmessage = function (e) {
  138. let res = JSON.parse(e.data);
  139. switch (res.type) {
  140. case 'online':
  141. let online_count = $('.online-box ul li').length - 1;
  142. let lis = '<li data-user="0,所有人">所有人</li>';
  143. if (online_count != res.count) {
  144. $.each(res.content, function (key, val) {
  145. lis += '<li data-user="' + val.uid + ',' + val.uname + '">' + val.uname + '</li>';
  146. })
  147. $('.online-box ul').html(lis);
  148. }
  149. break;
  150. case 'connect':
  151. case 'text':
  152. let str = '';
  153. if (res.user != undefined) {
  154. let user = res.user.from_uname || '';
  155. if (user != '') { str = '<font>'+user+'</font>'; }
  156. }
  157. showMsg(str+"<span>" + res.content + '</span>');
  158. break;
  159. default:
  160. showMsg("接收信息出错...");
  161. }
  162. //showMsg("<br>" + res.content);
  163. };
  164. /* 关闭连接 */
  165. socket.onclose = function () {
  166. state = socket.readyState;
  167. showMsg("连接已关闭或无法连接服务器..." + state);
  168. };
  169. } catch (exception) {
  170. showMsg("有错误发生...");
  171. }
  172. });
  173. /* 发送数据事件 */
  174. $("button:eq(2)").click(function () {
  175. if (state != 1) {
  176. alert(sockState());
  177. return;
  178. } else if ($("#content").val() == "") {
  179. alert("请输入发送内容!");
  180. return;
  181. }
  182. try {
  183. showMsg('<span class="self">' + $("#content").val() + '</span>');
  184. let data = { type: "text", from_uid: uid, from_uname: uname, to_uid: $("#to-uid").val(), to_uname: $("#to-uname").val(), content: $("#content").val() };
  185. socket.send(JSON.stringify(data));
  186. } catch (exception) {
  187. showMsg("发送数据出错...");
  188. }
  189. /* 清空文本框 */
  190. $("#content").val("");
  191. });
  192. /* 断开连接 */
  193. $("button:eq(1)").click(function () {
  194. try {
  195. showMsg("关闭连接...");
  196. socket.close();
  197. } catch (exception) {
  198. alert(sockState());
  199. }
  200. });
  201. //选择用户
  202. $('.online-box ul').on('click', 'li', function () {
  203. let arr = $(this).data('user').split(',');
  204. $('#to-uid').val(arr[0]);
  205. $('#to-uname').val(arr[1]);
  206. $('#to-uname-span').html(arr[1]);
  207. });
  208. });
  209. </script>
  210. </body>
  211. </html>


可以多人在线聊天,如下图演示:

上一篇:TP6 生成Qrcode二维码和Barcode条形码的方法

下一篇:TP6连接SQLite数据库及常用字段数据类型

讨论数量:0

请先登录再发表讨论。 2025-06-08

天涯网魂
3 杠 5 星
TA 的文章
TA 的随言
TA 的资源链