当前位置 知且行 php 正文 下一篇:

swoole框架-swoft实现程圣母与云天明对话功能

当万有引力号启动广播按钮,向宇宙发送三体星的坐标时,地球已经失去了任何侵略价值。三体人将所有在地球的资源全部撤走,但在临别时,安排了程圣母与云天明的远程会话。接下来,我们用swoft来实现他们两人的聊天功能。

光年之外的对话

PHP果真是宇宙最强的语言,星际通话也能办到…

对swoft不了解的同学请看 swoole框架-swoft初体验

启动ws服务

  1. swoft [master] sudo php bin/swoft ws:start
  2. Password:
  3. Server Information
  4. ************************************************************************************
  5. * WS | host: 0.0.0.0, port: 80, type: 1, worker: 1, mode: 3 (http is Enabled)
  6. * TCP | host: 0.0.0.0, port: 8099, type: 1, worker: 1 (Enabled)
  7. ************************************************************************************
  8. Server has been started. (master PID: 3954, manager PID: 3955)
  9. You can use CTRL + C to stop run.

创建http服务的聊天控制器

swoft 提供了生成控制器文件的命令行

  1. swoft [master] php bin/swoft gen:controller chat --prefix /chat -y
  2. Class data:
  3. {
  4. "name": "chat",
  5. "suffix": "Controller",
  6. "namespace": "App\\Controllers",
  7. "className": "ChatController",
  8. "prefix": "/chat",
  9. "idVar": "{id}"
  10. }

swoft/app/Controllers/ChatController.php

  1. <?php
  2. namespace App\Controllers;
  3. use Swoft\Http\Server\Bean\Annotation\Controller;
  4. use Swoft\Http\Server\Bean\Annotation\RequestMapping;
  5. use Swoft\Http\Server\Bean\Annotation\RequestMethod;
  6. /**
  7. * Class ChatController
  8. * @Controller(prefix="/chat")
  9. * @package App\Controllers
  10. */
  11. class ChatController{
  12. /**
  13. * @RequestMapping(route="/chat/{uid}", method=RequestMethod::GET)
  14. * @param int $uid
  15. * @return \Swoft\Http\Message\Server\Response|\think\response\View
  16. */
  17. public function index(int $uid)
  18. {
  19. $users = [
  20. 1 => '程心',
  21. 2 => '云天明',
  22. ];
  23. $receiveUid = $uid == 1 ? 2 : 1;
  24. $userName = $users[$uid];
  25. $data = compact('uid', 'userName', 'receiveUid');
  26. return view('chat/index', $data);
  27. }
  28. }

创建视图文件

视图文件用vue.js搭建,对于vue.js不熟悉的同学,参见 实例学习vue.js目录

swoft/resources/views/chat/index.php

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.0/css/bootstrap.min.css">
  6. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  7. </head>
  8. <style>
  9. .container {
  10. margin-top: 2%;
  11. }
  12. </style>
  13. <body>
  14. <div class="container">
  15. <div id="app">
  16. <h3 class="offset-md-3">{{ userName }}</h3>
  17. <div class="alert alert-warning col-sm-7">
  18. <strong>警告!</strong> 你们的聊天信息已经被全程监控...
  19. </div>
  20. <div v-for="(value, key) in list" :class="className(value.uid)" >
  21. {{ value.content }}
  22. </div>
  23. <div>
  24. <form action="" method="post" class="form-horizontal" role="form">
  25. <div class="form-group">
  26. <div class="col-sm-8">
  27. <input type="text" class="form-control" placeholder="远在光年之外的你们,想聊些什么呢..." v-model="content" required="required">
  28. </div>
  29. </div>
  30. <button type="submit" class="btn btn-info offset-md-6" @click.prevent="pushData">发送</button>
  31. </form>
  32. </div>
  33. </div>
  34. </div>
  35. <script>
  36. let ws = new WebSocket('ws://127.0.0.1/chat')
  37. ws.onopen = function() {
  38. let data = {
  39. 'sendUid': vm.$data.uid,
  40. 'type': 'bind',
  41. 'receiveUid': '',
  42. }
  43. // 将uid推送到服务端,与fd进行绑定
  44. ws.send(JSON.stringify(data));
  45. }
  46. ws.onmessage = function(evt) {
  47. let data = JSON.parse(evt.data)
  48. if (data.content) {
  49. vm.$data.list.push(data)
  50. }
  51. }
  52. ws.onclose = function() {
  53. console.log('连接关闭')
  54. }
  55. let vm = new Vue({
  56. el: "#app",
  57. data: {
  58. list: [],
  59. content: '',
  60. uid: <?=$uid?>,
  61. receiveUid: <?=$receiveUid?>,
  62. userName: '<?=$userName?>',
  63. },
  64. methods: {
  65. className(uid) {
  66. if (uid === this.uid) {
  67. return 'alert alert-success col-md-2'
  68. } else {
  69. return 'alert alert-danger col-md-2 offset-md-5'
  70. }
  71. },
  72. pushData() {
  73. let data = {
  74. 'sendUid': this.uid,
  75. 'receiveUid': this.receiveUid,
  76. 'content': this.content,
  77. 'type': 'chat'
  78. }
  79. ws.send(JSON.stringify(data));
  80. this.content = ''
  81. }
  82. },
  83. })
  84. </script>
  85. </body>
  86. </html>

创建ws控制器

  1. swoft [master] php bin/swoft gen:websocket chat --prefix /chat
  2. Class data:
  3. {
  4. "name": "chat",
  5. "suffix": "Controller",
  6. "namespace": "App\\WebSocket",
  7. "className": "ChatController",
  8. "prefix": "/chat"
  9. }
  1. <?php
  2. namespace App\WebSocket;
  3. use Swoft\Http\Message\Server\Request;
  4. use Swoft\Http\Message\Server\Response;
  5. use Swoft\WebSocket\Server\Bean\Annotation\WebSocket;
  6. use Swoft\WebSocket\Server\HandlerInterface;
  7. use Swoole\WebSocket\Frame;
  8. use Swoole\WebSocket\Server;
  9. /**
  10. * Class ChatController - This is an controller for handle websocket
  11. * @package App\WebSocket
  12. * @WebSocket("chat")
  13. */
  14. class ChatController implements HandlerInterface
  15. {
  16. /**
  17. * @param Request $request
  18. * @param Response $response
  19. * @return array
  20. * [
  21. * self::HANDSHAKE_OK,
  22. * $response
  23. * ]
  24. */
  25. public function checkHandshake(Request $request, Response $response): array
  26. {
  27. return [self::HANDSHAKE_OK, $response];
  28. }
  29. /**
  30. * @param Server $server
  31. * @param Request $request
  32. * @param int $fd
  33. * @return mixed
  34. */
  35. public function onOpen(Server $server, Request $request, int $fd)
  36. {
  37. }
  38. /**
  39. * @param Server $server
  40. * @param Frame $frame
  41. * @return mixed
  42. */
  43. public function onMessage(Server $server, Frame $frame)
  44. {
  45. $fd = $frame->fd;
  46. $data = json_decode($frame->data, true);
  47. if ($data['type'] == 'bind') {
  48. // 将uid与fd绑定
  49. $server->bind($fd, $data['sendUid']);
  50. }
  51. $start_fd = 0;
  52. while(true)
  53. {
  54. // 获取所有fd连接
  55. $conn_list = $server->getClientList($start_fd, 10);
  56. if ($conn_list === false or count($conn_list) === 0)
  57. {
  58. break;
  59. }
  60. $start_fd = end($conn_list);
  61. foreach($conn_list as $v)
  62. {
  63. // 根据fd获取uid
  64. $connection = $server->connection_info($v);
  65. if (isset($connection['uid']) && in_array($connection['uid'], [$data['receiveUid'], $data['sendUid']])) {
  66. if (isset($data['content'])) {
  67. $response = [
  68. 'type' => 'response',
  69. 'content' => $data['content'],
  70. 'uid' => $data['receiveUid'],
  71. ];
  72. if ($v != $fd) { // 避免重复发送给消息发起者的fd
  73. $server->push($v, json_encode($response, true));
  74. }
  75. }
  76. }
  77. }
  78. }
  79. // 推送消息给客户端
  80. \Swoft::$server->sendTo($fd, $frame->data);
  81. }
  82. /**
  83. * @param Server $server
  84. * @param int $fd
  85. * @return mixed
  86. */
  87. public function onClose(Server $server, int $fd)
  88. {
  89. // do something. eg. record log
  90. }
  91. }

结果演示

建议同时多开几个浏览器窗口,访问http://127.0.0.1/chat/1http://127.0.0.1/chat/2

切换窗口进行消息发送,观察其他窗口的数据变动

程心与云天明对话.gif

转载必须注明出处:https://www.zhiqiexing.com/56.html

关于我

我希望能成为一个认真、有趣、创造更多价值的人
关注微信
微信扫一扫关注我

微信扫一扫关注我

返回顶部