PHP+Socket系列之实现websocket聊天室

 3696

本篇文章给大家带来了关于php+socket的相关知识,其中主要介绍了怎么使用php原生socket实现一个简易的web聊天室?感兴趣的朋友下面一起来看一下,希望对大家有帮助。

php原生socket实现websocket聊天室

前言

这篇文章实现了使用php原生socket实现了一个简易的web聊天室,最终代码在文章最底部。

不出意外的话这应该是这个系列文章的最后一篇了,写这个系列文章时本以为是很简单的东西,但实际几篇写下来使我几乎通读了 workerman 的代码,所以永远不要眼高手低,一定还是要自己尝试,最好是写出来,才能证明自己真正的弄懂了一件事情

websocket介绍

webSocket 协议是一种网络通信协议,在 2008 年诞生,2011 年成为国际标准,RFC6455 定义了它的通信标准,如今所有浏览器都已支持了该协议。webSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工[^1]通讯的协议,服务器可以主动向客户端推送消息,客户端也可以主动向服务端发送消息。
webSocket 约定了一个通信协议的规范,通过握手机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似 tcp 的连接,从而方便 cs 通信。

为什么需要websocket

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了 请求 => 响应 模型,通信请求仅能由客户端发起,服务端对请求做出应答处理,这种通信模型有一个弊端:无法实现服务端主动向客户端发起消息。传统的 HTTP 请求,其并发能力都是依赖同时发起多个 TCP 连接访问服务器实现的而 websocket 则允许我们在一条 ws 连接上同时并发多个请求,即在 A 请求发出后 A 响应还未到达,就可以继续发出 B 请求。由于 TCP 的慢启动特性,以及连接本身的握手损耗,都使得 websocket 协议的这一特性有很大的效率提升。


PHP+Socket系列之实现websocket聊天室


特点

建立在 TCP 协议之上,服务端的实现相对比较容易

与 HTTP 协议有良好的兼容性,默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易被屏蔽,能通过各种 HTTP 代理服务器。

数据格式比较轻量,性能开销小,通信高效。

可以发送文本,也可以发送二进制数据。

没有同源限制,客户端可以与任意服务器进行通信。

协议标识符是 ws(如果加密则为 wss),服务地址就是 URL。


PHP实现websocket

客户端与服务端握手

websocket 协议在连接前需要握手[^2],通常握手方式有以下几种方式

基于 flash 的握手协议(不建议)

基于 md5 加密方式的握手协议:较早的握手方法,有两个 key,使用 md5 加密

基于 sha1 加密方式的握手协议

当前主要的握手协议,本文将以此协议为主

获取客户端上报的 Sec-WebSocket-key

拼接 key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

对字符串做 SHA1 计算,再把得到的结果通过 base64 加密,最后再返回给客户端

客户端请求信息如下:

  1. GET /chat HTTP/1.1Host: server.example.com
  2. Upgrade: websocket
  3. Connection: Upgrade
  4. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Origin: http://example.com
  5. Sec-WebSocket-Protocol: chat, superchat
  6. Sec-WebSocket-Version: 13

客户端需返回如下数据:

  1. HTTP/1.1 101 Switching Protocols
  2. Upgrade: websocket
  3. Sec-WebSocket-Version: 13Connection: Upgrade
  4. Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

我们根据此协议通过 PHP 方式实现:

  1. <?php
  2.  
  3. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  4. socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
  5. socket_bind($socket, 0, 8888);
  6. socket_listen($socket);
  7.  
  8. while (true) {
  9.     $conn_sock = socket_accept($socket);
  10.     $request = socket_read($conn_sock, 102400);
  11.  
  12.     $new_key = getShaKey($request);
  13.  
  14.     $response = "HTTP/1.1 101 Switching Protocols\r\n";
  15.     $response .= "Upgrade: websocket\r\n";
  16.     $response .= "Sec-WebSocket-Version: 13\r\n";
  17.     $response .= "Connection: Upgrade\r\n";
  18.     $response .= "Sec-WebSocket-Accept: {$new_key}\r\n\r\n";
  19.  
  20.     socket_write($conn_sock, $response);
  21. }
  22.  
  23. function getShaKey($request)
  24. {
  25.     // 获取 Sec-WebSocket-key
  26.     preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
  27.  
  28.     // 拼接 key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  29.     $new_key = trim($match[1]) . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
  30.  
  31.     // 对字符串做 `SHA1` 计算,再把得到的结果通过 `base64` 加密
  32.     return base64_encode(sha1($new_key, true));
  33. }

相关语法解释可参考 之前的文章,本文章不做详细介绍。

使用前端测试,打开我们的任意浏览器控制台(console)输入以下内容,返回的 websocket 对象的 readyState 为 1 即为握手成功,此为前端内容,本文不多做介绍,详情可参考 菜鸟教程

  1. console.log(new WebSocket('ws://192.162.2.166:8888'));
  2. // 运行后返回:
  3. WebSocket {
  4.     binaryType: "blob"
  5.     bufferedAmount: 0
  6.     extensions: ""
  7.     onclose: null
  8.     onerror: null
  9.     onmessage: null
  10.     onopen: null
  11.     protocol: ""
  12.     readyState: 1
  13.     url: "ws://192.162.2.166:8888/"
  14. }

发送数据与接收数据

使用 websocket 协议传输协议需要遵循特定的格式规范,详情请参考 datatracker.ietf.org/doc/html/rfc6...

PHP+Socket系列之实现websocket聊天室

为了方便,这里直接贴出加解密代码,以下代码借鉴与 workerman 的 src/Protocols/Websocket.php 文件:

  1. // 解码客户端发送的消息
  2. function decode($buffer)
  3. {
  4.     $len = \ord($buffer[1]) & 127;
  5.     if ($len === 126) {
  6.         $masks = \substr($buffer, 4, 4);
  7.         $data = \substr($buffer, 8);
  8.     } else {
  9.         if ($len === 127) {
  10.             $masks = \substr($buffer, 10, 4);
  11.             $data = \substr($buffer, 14);
  12.         } else {
  13.             $masks = \substr($buffer, 2, 4);
  14.             $data = \substr($buffer, 6);
  15.         }
  16.     }
  17.     $dataLength = \strlen($data);
  18.     $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
  19.     return $data ^ $masks;
  20. }
  21.  
  22. // 编码发送给客户端的消息
  23. function encode($buffer)
  24. {
  25.     if (!is_scalar($buffer)) {
  26.         throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
  27.     }
  28.     $len = \strlen($buffer);
  29.  
  30.     $first_byte = "\x81";
  31.  
  32.     if ($len <= 125) {
  33.         $encode_buffer = $first_byte . \chr($len) . $buffer;
  34.     } else {
  35.         if ($len <= 65535) {
  36.             $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
  37.         } else {
  38.             $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
  39.         }
  40.     }
  41.  
  42.     return $encode_buffer;
  43. }

我们修改刚才 客户端与服务端握手 阶段的代码,修改后全代码全文如下,该段代码实现了将客户端发送的消息转为大写后返回给客户端(当然只是为了演示):

  1. <?php
  2.  
  3. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  4. socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
  5. socket_bind($socket, 0, 8888);
  6. socket_listen($socket);
  7.  
  8. while (true) {
  9.     $conn_sock = socket_accept($socket);
  10.     $request = socket_read($conn_sock, 102400);
  11.  
  12.     $new_key = getShaKey($request);
  13.  
  14.     $response = "HTTP/1.1 101 Switching Protocols\r\n";
  15.     $response .= "Upgrade: websocket\r\n";
  16.     $response .= "Sec-WebSocket-Version: 13\r\n";
  17.     $response .= "Connection: Upgrade\r\n";
  18.     $response .= "Sec-WebSocket-Accept: {$new_key}\r\n\r\n";
  19.  
  20.     // 发送握手数据
  21.     socket_write($conn_sock, $response);
  22.  
  23.     // 新增内容,获取客户端发送的消息并转为大写还给客户端
  24.     $msg = socket_read($conn_sock, 102400);
  25.     socket_write($conn_sock, encode(strtoupper(decode($msg))));
  26. }
  27.  
  28. function getShaKey($request)
  29. {
  30.     // 获取 Sec-WebSocket-key
  31.     preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
  32.  
  33.     // 拼接 key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  34.     $new_key = trim($match[1]) . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
  35.  
  36.     // 对字符串做 `SHA1` 计算,再把得到的结果通过 `base64` 加密
  37.     return base64_encode(sha1($new_key, true));
  38. }
  39.  
  40. function decode($buffer)
  41. {
  42.     $len = \ord($buffer[1]) & 127;
  43.     if ($len === 126) {
  44.         $masks = \substr($buffer, 4, 4);
  45.         $data = \substr($buffer, 8);
  46.     } else {
  47.         if ($len === 127) {
  48.             $masks = \substr($buffer, 10, 4);
  49.             $data = \substr($buffer, 14);
  50.         } else {
  51.             $masks = \substr($buffer, 2, 4);
  52.             $data = \substr($buffer, 6);
  53.         }
  54.     }
  55.     $dataLength = \strlen($data);
  56.     $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
  57.     return $data ^ $masks;
  58. }
  59.  
  60. function encode($buffer)
  61. {
  62.     if (!is_scalar($buffer)) {
  63.         throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
  64.     }
  65.     $len = \strlen($buffer);
  66.  
  67.     $first_byte = "\x81";
  68.  
  69.     if ($len <= 125) {
  70.         $encode_buffer = $first_byte . \chr($len) . $buffer;
  71.     } else {
  72.         if ($len <= 65535) {
  73.             $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
  74.         } else {
  75.             $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
  76.         }
  77.     }
  78.  
  79.     return $encode_buffer;
  80. }

使用 在线测试工具 进行测试,可以看到消息已经可以正常发送接收,接下来的文章将继续优化代码,实现简易聊天室,敬请关注:


PHP+Socket系列之实现websocket聊天室


实现web聊天室

我们紧接着上文的代码继续优化,以实现简易的web聊天室

多路复用

其实就是加一下 socket_select() 函数,本文就不写原理与语法了,详情可参考 之前的文章,以下代码修改自前文 发送数据与接收数据

  1. ...
  2.  
  3. socket_listen($socket);
  4.  
  5. +$sockets[] = $socket;
  6. +$user = [];
  7. while (true) {
  8. +   $tmp_sockets = $sockets;
  9. +   socket_select($tmp_sockets, $write, $except, null);
  10.  
  11. +   foreach ($tmp_sockets as $sock) {
  12. +       if ($sock == $socket) {
  13. +           $sockets[] = socket_accept($socket);
  14. +           $user[] = ['socket' => $socket, 'handshake' => false];
  15. +       } else {
  16. +           $curr_user = $user[array_search($sock, $user)];
  17. +           if ($curr_user['handshake']) { // 已握手
  18. +               $msg = socket_read($sock, 102400);
  19. +               echo '客户端发来消息' . decode($msg);
  20. +               socket_write($sock, encode('这是来自服务端的消息'));
  21. +           } else {
  22. +               // 握手
  23. +           }
  24. +       }
  25. +   }
  26.  
  27. -   $conn_sock = socket_accept($socket);
  28. -   $request = socket_read($conn_sock, 102400);
  29.  
  30. ...


实现聊天室

最终成果演示


PHP+Socket系列之实现websocket聊天室


我们将上述代码改造成类,并在类变量储存用户信息,添加消息处理等逻辑,最后贴出代码,建议保存下来自己尝试一下,也许会有全新的认知,后端代码:

  1. <?php
  2.  
  3. new WebSocket();
  4.  
  5. class Websocket
  6. {
  7.     /**
  8.      * @var resource
  9.      */
  10.     protected $socket;
  11.  
  12.     /**
  13.      * @var array 用户列表
  14.      */
  15.     protected $user = [];
  16.  
  17.     /**
  18.      * @var array 存放所有 socket 资源
  19.      */
  20.     protected $socket_list = [];
  21.  
  22.     public function __construct()
  23.     {
  24.         $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  25.         socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, true);
  26.         socket_bind($this->socket, 0, 8888);
  27.         socket_listen($this->socket);
  28.  
  29.         // 将 socket 资源放入 socket_list
  30.         $this->socket_list[] = $this->socket;
  31.  
  32.         while (true) {
  33.             $tmp_sockets = $this->socket_list;
  34.             socket_select($tmp_sockets, $write, $except, null);
  35.  
  36.             foreach ($tmp_sockets as $sock) {
  37.                 if ($sock == $this->socket) {
  38.                     $conn_sock = socket_accept($sock);
  39.                     $this->socket_list[] = $conn_sock;
  40.                     $this->user[] = ['socket' => $conn_sock, 'handshake' => false, 'name' => '无名氏'];
  41.                 } else {
  42.                     $request = socket_read($sock, 102400);
  43.                     $k = $this->getUserIndex($sock);
  44.  
  45.                     if (!$request) {
  46.                         continue;
  47.                     }
  48.  
  49.                     // 用户端断开连接
  50.                     if ((\ord($request[0]) & 0xf) == 0x8) {
  51.                         $this->close($k);
  52.                         continue;
  53.                     }
  54.  
  55.                     if (!$this->user[$k]['handshake']) {
  56.                         // 握手
  57.                         $this->handshake($k, $request);
  58.                     } else {
  59.                         // 已握手
  60.                         $this->send($k, $request);
  61.                     }
  62.                 }
  63.             }
  64.         }
  65.     }
  66.  
  67.     /**
  68.      * 关闭连接
  69.      *
  70.      * @param $k
  71.      */
  72.     protected function close($k)
  73.     {
  74.         $u_name = $this->user[$k]['name'] ?? '无名氏';
  75.         socket_close($this->user[$k]['socket']);
  76.         $socket_key = array_search($this->user[$k]['socket'], $this->socket_list);
  77.         unset($this->socket_list[$socket_key]);
  78.         unset($this->user[$k]);
  79.  
  80.         $user = [];
  81.         foreach ($this->user as $v) {
  82.             $user[] = $v['name'];
  83.         }
  84.         $res = [
  85.             'type' => 'close',
  86.             'users' => $user,
  87.             'msg' => $u_name . '已退出',
  88.             'time' => date('Y-m-d H:i:s')
  89.         ];
  90.         $this->sendAllUser($res);
  91.     }
  92.  
  93.     /**
  94.      * 获取用户索引
  95.      *
  96.      * @param $socket
  97.      * @return int|string
  98.      */
  99.     protected function getUserIndex($socket)
  100.     {
  101.         foreach ($this->user as $k => $v) {
  102.             if ($v['socket'] == $socket) {
  103.                 return $k;
  104.             }
  105.         }
  106.     }
  107.  
  108.     /**
  109.      * 握手
  110.      * @param $k
  111.      * @param $request
  112.      */
  113.     protected function handshake($k, $request)
  114.     {
  115.         preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
  116.         $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
  117.  
  118.         $response = "HTTP/1.1 101 Switching Protocols\r\n";
  119.         $response .= "Upgrade: websocket\r\n";
  120.         $response .= "Connection: Upgrade\r\n";
  121.         $response .= "Sec-WebSocket-Accept: {$key}\r\n\r\n";
  122.         socket_write($this->user[$k]['socket'], $response);
  123.         $this->user[$k]['handshake'] = true;
  124.     }
  125.  
  126.     /**
  127.      * 接收并处理消息
  128.      *
  129.      * @param $k
  130.      * @param $msg
  131.      */
  132.     public function send($k, $msg)
  133.     {
  134.         $msg = $this->decode($msg);
  135.         $msg = json_decode($msg, true);
  136.  
  137.         if (!isset($msg['type'])) {
  138.             return;
  139.         }
  140.  
  141.         switch ($msg['type']) {
  142.             case 'login': // 登录
  143.                 $this->user[$k]['name'] = $msg['name'] ?? '无名氏';
  144.                 $users = [];
  145.                 foreach ($this->user as $v) {
  146.                     $users[] = $v['name'];
  147.                 }
  148.                 $res = [
  149.                     'type' => 'login',
  150.                     'name' => $this->user[$k]['name'],
  151.                     'msg' => $this->user[$k]['name'] . ': login success',
  152.                     'users' => $users,
  153.                 ];
  154.                 $this->sendAllUser($res);
  155.                 break;
  156.             case 'message': // 接收并发送消息
  157.                 $res = [
  158.                     'type' => 'message',
  159.                     'name' => $this->user[$k]['name'] ?? '无名氏',
  160.                     'msg' => $msg['msg'],
  161.                     'time' => date('H:i:s'),
  162.                 ];
  163.                 $this->sendAllUser($res);
  164.                 break;
  165.         }
  166.     }
  167.  
  168.     /**
  169.      * 发送给所有人
  170.      *
  171.      */
  172.     protected function sendAllUser($msg)
  173.     {
  174.         if (is_array($msg)) {
  175.             $msg = json_encode($msg);
  176.         }
  177.  
  178.         $msg = $this->encode($msg);
  179.  
  180.         foreach ($this->user as $k => $v) {
  181.             socket_write($v['socket'], $msg, strlen($msg));
  182.         }
  183.     }
  184.  
  185.     /**
  186.      * 解码
  187.      *
  188.      * @param $buffer
  189.      * @return string
  190.      */
  191.     protected function decode($buffer)
  192.     {
  193.         $len = \ord($buffer[1]) & 127;
  194.         if ($len === 126) {
  195.             $masks = \substr($buffer, 4, 4);
  196.             $data = \substr($buffer, 8);
  197.         } else {
  198.             if ($len === 127) {
  199.                 $masks = \substr($buffer, 10, 4);
  200.                 $data = \substr($buffer, 14);
  201.             } else {
  202.                 $masks = \substr($buffer, 2, 4);
  203.                 $data = \substr($buffer, 6);
  204.             }
  205.         }
  206.         $dataLength = \strlen($data);
  207.         $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
  208.         return $data ^ $masks;
  209.     }
  210.  
  211.     protected function encode($buffer)
  212.     {
  213.         if (!is_scalar($buffer)) {
  214.             throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
  215.         }
  216.         $len = \strlen($buffer);
  217.  
  218.         $first_byte = "\x81";
  219.  
  220.         if ($len <= 125) {
  221.             $encode_buffer = $first_byte . \chr($len) . $buffer;
  222.         } else {
  223.             if ($len <= 65535) {
  224.                 $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
  225.             } else {
  226.                 $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
  227.             }
  228.         }
  229.  
  230.         return $encode_buffer;
  231.     }
  232. }

前端代码如下(前端内容不在本文讨论范围之内,具体可参考 菜鸟教程):

  1. <!doctype html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport"
  6.           content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  7.     <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8.     <title>Document</title>
  9. </head>
  10. <style>
  11.     * {
  12.         margin: 0;
  13.         padding: 0;
  14.     }
  15.     h3 {
  16.         display: flex;
  17.         justify-content: center;
  18.         margin: 30px auto;
  19.     }
  20.     .but-box {
  21.         border-radius: 5px;
  22.         display: flex;
  23.         justify-content: center;
  24.         align-items: center;
  25.         margin-top: 10px;
  26.     }
  27.     #box {
  28.         display: flex;
  29.         margin: 5px auto;
  30.         border-radius: 5px;
  31.         border: 1px #ccc solid;
  32.         height: 400px;
  33.         width: 700px;
  34.         overflow-y: auto;
  35.         overflow-x: hidden;
  36.         position: relative;
  37.     }
  38.     #msg-box {
  39.         width: 480px;
  40.         margin-right: 111px;
  41.         height: 100%;
  42.         overflow-y: auto;
  43.         overflow-x: hidden;
  44.     }
  45.     #user-box {
  46.         width: 110px;
  47.         overflow-y: auto;
  48.         overflow-x: hidden;
  49.         float: left;
  50.         border-left: 1px #ccc solid;
  51.         height: 100%;
  52.         background-color: #F1F1F1;
  53.     }
  54.     button {
  55.         float: right;
  56.         width: 80px;
  57.         height: 35px;
  58.         font-size: 18px;
  59.     }
  60.     input {
  61.         width: 100%;
  62.         height: 30px;
  63.         padding: 2px;
  64.         line-height: 20px;
  65.         outline: none;
  66.         border: solid 1px #CCC;
  67.     }
  68.     .but-box p {
  69.         margin-right: 160px;
  70.     }
  71. </style>
  72. <body>
  73.  
  74. <h3>这是一个php socket实现的web聊天室</h3>
  75.  
  76. <div id="box">
  77.     <div id="msg-box"></div>
  78.     <div id="user-box"></div>
  79. </div>
  80.  
  81. <div>
  82.  
  83.     <p><textarea cols="60" rows="3" style="resize:none;pedding: 10px"    id="content"> </textarea></p>
  84.     <button id="send">发送</button>
  85. </div>
  86. <script src="https://cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
  87. <script>
  88.     let ws = new WebSocket('ws://124.222.85.67:8888');
  89.  
  90.     ws.onopen = function (event) {
  91.         console.log('连接成功');
  92.  
  93.         var name = prompt('请输入用户名:');
  94.  
  95.         ws.send(JSON.stringify({
  96.             type: 'login',
  97.             name: name
  98.         }));
  99.  
  100.         if (!name) {
  101.             alert('好你个坏蛋,竟然没有输入用户名');
  102.         }
  103.     };
  104.     ws.onmessage = function (event) {
  105.         let data = JSON.parse(event.data);
  106.         console.log(data);
  107.  
  108.         switch (data.type) {
  109.             case 'close':
  110.             case 'login':
  111.                 $("#user-box").html('');
  112.                 data.users.forEach(function (item) {
  113.                     $("#user-box").append(`<p style="color: grey;">${item}</p>`);
  114.                 });
  115.                 if (data.msg) {
  116.                     $("#msg-box").append(`<p style="color: grey;">${data.msg}</p>`);
  117.                 }
  118.                 break;
  119.             case 'message':
  120.                 $("#msg-box").append(`<p><span style="color: #0A89FF">${data.time}</span><span style="color: red">${data.name}</span>${data.msg}</p>`);
  121.                 break;
  122.         }
  123.     };
  124.  
  125.     ws.onclose = function (event) {
  126.         alert('连接关闭');
  127.     };
  128.  
  129.     document.onkeydown = function (event) {
  130.         if (event.keyCode == 13) {
  131.             send();
  132.         }
  133.     }
  134.  
  135.     $("#send").click(function () {
  136.         send();
  137.     });
  138.  
  139.     function send() {
  140.         let content = $("#content").val();
  141.         $("#content").val('');
  142.         if (!content) {
  143.             return;
  144.         }
  145.         ws.send(JSON.stringify({
  146.             type: 'message',
  147.             msg: content
  148.         }));
  149.     }
  150. </script>
  151. </body>
  152. </html>

1、是通讯传输的一个术语。 通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合

2、为了建立 websocket 连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)


本文网址:https://www.zztuku.com/detail-13750.html
站长图库 - PHP+Socket系列之实现websocket聊天室
申明:本文转载于《learnku》,如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐

    Illustrator制作电视台标志