Blogger Information
Blog 4
fans 0
comment 0
visits 4332
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
利用 webSocket 与 Swoole 打造一个小型聊天室(协程)
谨言慎行
Original
874 people have browsed it

前言

    前面有写一个异步简单的聊天室,然后想着,就把协程的也弄了吧所以就有了这个文章,其实所有的功能都大差不差,就仅仅几个地方不一样而已,也都是简单的地方。
博文地址:利用 webSocket 与 Swoole 打造一个小型聊天室 (异步)
本次也没增加功能,就是增加了一个心跳,从前端定时发送一个 ping ,服务端不作反应,仅此而已。

前端页面代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>打工人聊天室</title>
  6. <!--需要引入jq 文件-->
  7. </head>
  8. <style>
  9. .content {
  10. height: 400px;
  11. max-width: 400px;
  12. overflow: auto;
  13. border-radius: 5px;
  14. border: 1px solid #f0f0f0;
  15. }
  16. </style>
  17. <body>
  18. <div id="content" class="content">
  19. <p>聊天区域</p>
  20. </div>
  21. 你好打工人:<samp id="nickname">昵称</samp> <br>
  22. 本次连接FD: <samp id="fd-samp"></samp> <br>
  23. <input type="text" id="msg">
  24. <input type="hidden" id="fd" value="">
  25. <button id="send" onclick="send()">发送</button>
  26. </body>
  27. </html>

JS 代码:

    在服务器信息回执时,会有第一次连接回执,还是服务端发送消息回执的状态区别,通过 msgType 来分辨,如果是第一次连接的回执消息,则把 FD 做一个页面留存,并不显示在聊天消息区,如果收到的是消息回执,就直接显示到聊天消息区。

    还有就是,前后端相互通信发送的东西,都是字符串性质最优,我前端处理的方法是先组合成一个对象,然后转 JSON 串。

  1. <script>
  2. //滚动条最底部
  3. function scrolltest() {
  4. var div = document.getElementById("content");
  5. div.scrollTop = div.scrollHeight;
  6. }
  7. var wsServer = 'ws://127.0.0.1:9502/websocket';
  8. var websocket = new WebSocket(wsServer);
  9. var nickname = Math.random().toString(36).substr(2);
  10. thisFd = '';
  11. $('#nickname').html(nickname);
  12. //点击发送
  13. function send() {
  14. var msg = $('#msg').val();
  15. var data = {
  16. 'nickname': nickname,
  17. 'fd': thisFd,
  18. 'data': msg
  19. }
  20. //生成json 方便后台接收以及使用
  21. var data = JSON.stringify(data);
  22. websocket.send(data);
  23. //然后清空
  24. $('#msg').val('');
  25. }
  26. //链接成功
  27. websocket.onopen = function (evt) {
  28. var data = {
  29. 'msgType': 'open'
  30. }
  31. var data = JSON.stringify(data);
  32. $("#content >p:last-child").after('<p> 服务器已连接,开始聊天吧 </p>');
  33. websocket.send(data);
  34. };
  35. //链接断开
  36. websocket.onclose = function (evt) {
  37. $("#content >p:last-child").after('<p> 服务器已断开,请重新连接 </p>');
  38. };
  39. //收到服务器消息
  40. websocket.onmessage = function (evt) {
  41. //握手成功后,会接受到服务端返回的fd ,msgType = 1
  42. //字符串格式化成json
  43. var data = eval('(' + evt.data + ')');
  44. // console.log(evt.data);
  45. switch (data.msgType) {
  46. case 1:
  47. thisFd = data.fd;
  48. $('#fd-samp').html(thisFd);
  49. $('#fd').val(thisFd);
  50. break;
  51. case 2:
  52. if (data.nickname == nickname) {
  53. data.nickname = '我';
  54. }
  55. $("#content >p:last-child").after('<p>' + data.nickname + ' 在 ' + data.time + ' 说:<br>' + data.data + '</p>');
  56. //接收到消息自动触底
  57. scrolltest();
  58. break;
  59. }
  60. };
  61. //服务器异常
  62. websocket.onerror = function (evt, e) {
  63. $("#content >p:last-child").after('<p> 服务器异常 </p>');
  64. };
  65. //心跳,本次新增
  66. function heartbeat() {
  67. var data = {
  68. 'msgType': 'ping',
  69. }
  70. //生成json 方便后台接收以及使用
  71. var data = JSON.stringify(data);
  72. websocket.send(data);
  73. }
  74. //30 秒一次
  75. setInterval(heartbeat, 30000);
  76. </script>

服务端代码
协程,都需要在 Co\run(function () {}) 里。

  1. <?php
  2. //定义获取当前的id函数
  3. function getObjectId(\Swoole\Http\Response $response) {
  4. if (PHP_VERSION_ID < 70200) {
  5. $id = spl_object_hash($response);
  6. } else {
  7. $id = spl_object_id($response);
  8. }
  9. return $id;
  10. }
  11. Co\run(function () {
  12. $server = new Co\Http\Server('127.0.0.1', 9502, false);
  13. $server->set([
  14. 'heartbeat_idle_time' => 600, // 表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭
  15. 'heartbeat_check_interval' => 60, // 表示每60秒遍历一次
  16. ]);
  17. $server->handle('/websocket', function ($request, $ws) {
  18. $ws->upgrade();
  19. global $wsObjects;
  20. $objectId = getObjectId($ws);
  21. $wsObjects[$objectId] = $ws;
  22. while (true) {
  23. $frame = $ws->recv();
  24. if ($frame === '') {
  25. unset($wsObjects[$objectId]);
  26. $ws->close();
  27. break;
  28. } else if ($frame === false) {
  29. echo 'error : ' . swoole_last_error() . "\n";
  30. break;
  31. } else {
  32. if ($frame->data == 'close' || get_class($frame) === Swoole\WebSocket\CloseFrame::class) {
  33. unset($wsObjects[$objectId]);
  34. $ws->close();
  35. return;
  36. }
  37. //格式化接收到json
  38. $data = json_decode($frame->data);
  39. switch ($data->msgType){
  40. case 'open':
  41. //链接第一次
  42. $data = json_encode([
  43. 'fd' => $objectId,
  44. 'msgType' => 1 //代表第一次连接,前端处理fd
  45. ]);
  46. $ws->push($data);
  47. break;
  48. case 'ping':
  49. //接收到心跳 不作回复
  50. // echo $data->msgType;
  51. break;
  52. default :
  53. // 原基础上不动,增加一些自定义
  54. $data->msgType = 2; //代表服务器端回复
  55. $data->time = date('Y-m-d H-i-s');
  56. $data = json_encode($data);
  57. foreach ($wsObjects as $obj) {
  58. $obj->push($data);
  59. }
  60. }
  61. }
  62. }
  63. });
  64. $server->start();
  65. });

代码齐全之后,接下来就只需要在控制台执行以下 PHP 文件就行。

然后前台直接访问你的网站地址,我的是本地 127.0.0.1

多开几个窗口模拟多个用户,然后发送消息测试即可:

你好,打工人。

代码很简单,难度不大,但是可以很简洁的反应出 webScoket 和 Swoole 的一种强大。

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post