Table des matières
特点
PHP实现websocket
实现web聊天室
实现聊天室
这是一个php socket实现的web聊天室
Maison développement back-end tutoriel php La série PHP+Socket implémente la salle de discussion Websocket

La série PHP+Socket implémente la salle de discussion Websocket

Feb 02, 2023 pm 04:39 PM
php websocket socket

Cet article vous apporte des connaissances pertinentes sur php+socket. Il présente principalement comment utiliser le socket natif php pour implémenter une simple salle de discussion Web ? Les amis intéressés peuvent jeter un œil ci-dessous. J'espère que cela sera utile à tout le monde. Le socket natif OPhp réalise la salle de discussion WebSocket

Avant-propos

Cet article réalise l'utilisation du socket natif PHP pour réaliser une salle de discussion Web simple, et le code final se trouve au bas de l'article.

À tout le moins, cela devrait être le dernier article de cette série d'articles. Quand j'ai écrit cette série d'articles, je pensais que ce serait une chose très simple, mais après avoir écrit ces quelques articles, j'ai presque lu le code de Workerman, alors ne soyez jamais trop vaniteux, vous devez l'essayer vous-même, de préférence l'écrire, pour prouver que vous comprenez vraiment quelque chose

Introduction à websocket

Le protocole webSocket est un protocole de communication réseau Il est né en 2008. et est devenu un standard international en 2011. Le standard

RFC6455

définit son standard de communication et est désormais pris en charge par tous les navigateurs. webSocket est un protocole de communication en duplex intégral sur une seule connexion TCP que HTML5 a commencé à fournir. Le serveur peut envoyer activement des messages au client, et le client peut également envoyer activement des messages au serveur. webSocket stipule la spécification d'un protocole de communication. Grâce au mécanisme de prise de contact, une connexion de type TCP peut être établie entre le client (navigateur) et le serveur (serveur Web), facilitant ainsi la communication CS.

Pourquoi avons-nous besoin de websocket

Le protocole HTTP est un protocole de couche application unidirectionnel, sans état et sans connexion. Il adopte le modèle Request=>Response. Les requêtes de communication ne peuvent être initiées que par le client, et le serveur répond à la requête. Ce modèle de communication présente un inconvénient : il est impossible pour le serveur d'envoyer de manière proactive. messages au client. Le client lance un message. Les capacités de concurrence des requêtes HTTP traditionnelles sont obtenues en initiant plusieurs connexions TCP pour accéder au serveur en même temps. Websocket nous permet d'émettre simultanément plusieurs requêtes sur une connexion ws. Autrement dit, une fois la requête A envoyée, la réponse A a été envoyée. pas encore arrivé. Vous pouvez continuer à émettre des demandes B. En raison de la fonctionnalité de démarrage lent de TCP et de la perte de connexion de la connexion elle-même, cette fonctionnalité du protocole websocket a considérablement amélioré l'efficacité.

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

La série PHP+Socket implémente la salle de discussion 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

      Fonctionnalités
      • est construit sur le protocole TCP, et la mise en œuvre du serveur est relativement simple

    • a une bonne compatibilité avec le HTTP protocole, par défaut Les ports sont également 80 et 443, et le protocole HTTP est utilisé dans la phase de prise de contact, il n'est donc pas facilement bloqué pendant la prise de contact et peut passer par divers serveurs proxy HTTP.

  • Le format de données est relativement léger, a une faible surcharge de performances et une communication efficace.

  • Vous pouvez envoyer du texte ou des données binaires.

  • Il n'y a pas de restriction de même origine, le client peut communiquer avec n'importe quel serveur.
  • L'identifiant du protocole est ws (ou wss si crypté), et l'adresse du service est l'URL.

    PHP implémente websocketHandshake client et serveur

    🎜Le protocole websocket nécessite une handshake avant la connexion [^2], il existe généralement les méthodes de handshake suivantes🎜
    • 🎜basées sur le protocole flash Handshake (non recommandé)🎜🎜
    • 🎜Protocole de prise de contact basé sur la méthode de cryptage md5🎜🎜Méthode de prise de contact antérieure, avec deux clés, utilisant le cryptage md5🎜🎜
    • 🎜Protocole de prise de contact basé sur la méthode de cryptage sha1🎜🎜 Le protocole de prise de contact principal actuel , cet article se concentrera sur ce protocole🎜
      • 🎜Récupérez la Sec-WebSocket-key rapportée par le client 🎜🎜
      • 🎜Splice key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11🎜🎜
      • 🎜Faites SHA1 à la chaîne Calculer, puis crypter le résultat via base64, et enfin le renvoyer au client 🎜🎜🎜🎜🎜🎜Les informations de la demande du client sont les suivantes : 🎜
        GET /chat HTTP/1.1Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13
        Copier après la connexion
        🎜Le client doit renvoyer les données suivantes : 🎜
        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Sec-WebSocket-Version: 13Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Copier après la connexion
        🎜us Implémenté via PHP selon ce protocole : 🎜
        <?php
        
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
        socket_bind($socket, 0, 8888);
        socket_listen($socket);
        
        while (true) {
            $conn_sock = socket_accept($socket);
            $request = socket_read($conn_sock, 102400);
        
            $new_key = getShaKey($request);
        
            $response = "HTTP/1.1 101 Switching Protocols\r\n";
            $response .= "Upgrade: websocket\r\n";
            $response .= "Sec-WebSocket-Version: 13\r\n";
            $response .= "Connection: Upgrade\r\n";
            $response .= "Sec-WebSocket-Accept: {$new_key}\r\n\r\n";
        
            socket_write($conn_sock, $response);
        }
        
        function getShaKey($request)
        {
            // 获取 Sec-WebSocket-key
            preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
        
            // 拼接 key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
            $new_key = trim($match[1]) . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;;
        
            // 对字符串做 `SHA1` 计算,再把得到的结果通过 `base64` 加密
            return base64_encode(sha1($new_key, true));
        }
        Copier après la connexion
        🎜Les explications de la syntaxe associée peuvent être trouvées dans les 🎜articles précédents🎜, cet article ne la présentera pas en détail. 🎜🎜Utilisez les tests front-end, ouvrez l'une de nos consoles de navigateur et entrez le contenu suivant. L'état prêt de l'objet websocket renvoyé est 1, ce qui signifie que la poignée de main est réussie. Il s'agit du contenu front-end. en détail. Pour plus de détails, veuillez vous référer au 🎜Tutoriel Rookie🎜 : 🎜
        console.log(new WebSocket(&#39;ws://192.162.2.166:8888&#39;));
        // 运行后返回:
        WebSocket {
            binaryType: "blob"
            bufferedAmount: 0
            extensions: ""
            onclose: null
            onerror: null
            onmessage: null
            onopen: null
            protocol: ""
            readyState: 1
            url: "ws://192.162.2.166:8888/"}
        Copier après la connexion
        🎜🎜🎜🎜Envoi et réception de données🎜🎜

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

        La série PHP+Socket implémente la salle de discussion Websocket

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

        // 解码客户端发送的消息
        function decode($buffer)
        {
            $len = \ord($buffer[1]) & 127;
            if ($len === 126) {
                $masks = \substr($buffer, 4, 4);
                $data = \substr($buffer, 8);
            } else {
                if ($len === 127) {
                    $masks = \substr($buffer, 10, 4);
                    $data = \substr($buffer, 14);
                } else {
                    $masks = \substr($buffer, 2, 4);
                    $data = \substr($buffer, 6);
                }
            }
            $dataLength = \strlen($data);
            $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
            return $data ^ $masks;
        }
        
        // 编码发送给客户端的消息
        function encode($buffer)
        {
            if (!is_scalar($buffer)) {
                throw new \Exception("You can&#39;t send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
            }
            $len = \strlen($buffer);
        
            $first_byte = "\x81";
        
            if ($len <= 125) {
                $encode_buffer = $first_byte . \chr($len) . $buffer;
            } else {
                if ($len <= 65535) {
                    $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
                } else {
                    $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
                }
            }
        
            return $encode_buffer;
        }
        Copier après la connexion

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

        <?php
        
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
        socket_bind($socket, 0, 8888);
        socket_listen($socket);
        
        while (true) {
            $conn_sock = socket_accept($socket);
            $request = socket_read($conn_sock, 102400);
        
            $new_key = getShaKey($request);
        
            $response = "HTTP/1.1 101 Switching Protocols\r\n";
            $response .= "Upgrade: websocket\r\n";
            $response .= "Sec-WebSocket-Version: 13\r\n";
            $response .= "Connection: Upgrade\r\n";
            $response .= "Sec-WebSocket-Accept: {$new_key}\r\n\r\n";
        
            // 发送握手数据
            socket_write($conn_sock, $response);
        
            // 新增内容,获取客户端发送的消息并转为大写还给客户端
            $msg = socket_read($conn_sock, 102400);
            socket_write($conn_sock, encode(strtoupper(decode($msg))));
        }
        
        function getShaKey($request)
        {
            // 获取 Sec-WebSocket-key
            preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
        
            // 拼接 key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
            $new_key = trim($match[1]) . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;;
        
            // 对字符串做 `SHA1` 计算,再把得到的结果通过 `base64` 加密
            return base64_encode(sha1($new_key, true));
        }
        
        function decode($buffer)
        {
            $len = \ord($buffer[1]) & 127;
            if ($len === 126) {
                $masks = \substr($buffer, 4, 4);
                $data = \substr($buffer, 8);
            } else {
                if ($len === 127) {
                    $masks = \substr($buffer, 10, 4);
                    $data = \substr($buffer, 14);
                } else {
                    $masks = \substr($buffer, 2, 4);
                    $data = \substr($buffer, 6);
                }
            }
            $dataLength = \strlen($data);
            $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
            return $data ^ $masks;
        }
        
        function encode($buffer)
        {
            if (!is_scalar($buffer)) {
                throw new \Exception("You can&#39;t send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
            }
            $len = \strlen($buffer);
        
            $first_byte = "\x81";
        
            if ($len <= 125) {
                $encode_buffer = $first_byte . \chr($len) . $buffer;
            } else {
                if ($len <= 65535) {
                    $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
                } else {
                    $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
                }
            }
        
            return $encode_buffer;
        }
        Copier après la connexion

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

        La série PHP+Socket implémente la salle de discussion Websocket

        实现web聊天室

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

        多路复用

        其实就是加一下 socket_select() 函数 La série PHP+Socket implémente la salle de discussion Websocket ,本文就不写原理与语法了,详情可参考 之前的文章,以下代码修改自前文 La série PHP+Socket implémente la salle de discussion Websocket

        ...
        
        socket_listen($socket);
        
        +$sockets[] = $socket;
        +$user = [];
        while (true) {
        +   $tmp_sockets = $sockets;
        +   socket_select($tmp_sockets, $write, $except, null);
        
        +   foreach ($tmp_sockets as $sock) {
        +       if ($sock == $socket) {
        +           $sockets[] = socket_accept($socket);
        +           $user[] = [&#39;socket&#39; => $socket, &#39;handshake&#39; => false];
        +       } else {
        +           $curr_user = $user[array_search($sock, $user)];
        +           if ($curr_user[&#39;handshake&#39;]) { // 已握手
        +               $msg = socket_read($sock, 102400);
        +               echo &#39;客户端发来消息&#39; . decode($msg);
        +               socket_write($sock, encode(&#39;这是来自服务端的消息&#39;));
        +           } else {
        +               // 握手
        +           }
        +       }
        +   }
        
        -   $conn_sock = socket_accept($socket);
        -   $request = socket_read($conn_sock, 102400);
        
        ...
        Copier après la connexion

        实现聊天室

        最终成果演示

        La série PHP+Socket implémente la salle de discussion Websocket

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

        <?php
        
        new WebSocket();
        
        class Websocket
        {
            /**
             * @var resource
             */
            protected $socket;
        
            /**
             * @var array 用户列表
             */
            protected $user = [];
        
            /**
             * @var array 存放所有 socket 资源
             */
            protected $socket_list = [];
        
            public function __construct()
            {
                $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
                socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, true);
                socket_bind($this->socket, 0, 8888);
                socket_listen($this->socket);
        
                // 将 socket 资源放入 socket_list
                $this->socket_list[] = $this->socket;
        
                while (true) {
                    $tmp_sockets = $this->socket_list;
                    socket_select($tmp_sockets, $write, $except, null);
        
                    foreach ($tmp_sockets as $sock) {
                        if ($sock == $this->socket) {
                            $conn_sock = socket_accept($sock);
                            $this->socket_list[] = $conn_sock;
                            $this->user[] = [&#39;socket&#39; => $conn_sock, &#39;handshake&#39; => false, &#39;name&#39; => &#39;无名氏&#39;];
                        } else {
                            $request = socket_read($sock, 102400);
                            $k = $this->getUserIndex($sock);
        
                            if (!$request) {
                                continue;
                            }
        
                            // 用户端断开连接
                            if ((\ord($request[0]) & 0xf) == 0x8) {
                                $this->close($k);
                                continue;
                            }
        
                            if (!$this->user[$k][&#39;handshake&#39;]) {
                                // 握手
                                $this->handshake($k, $request);
                            } else {
                                // 已握手
                                $this->send($k, $request);
                            }
                        }
                    }
                }
            }
        
            /**
             * 关闭连接
             *
             * @param $k
             */
            protected function close($k)
            {
                $u_name = $this->user[$k][&#39;name&#39;] ?? &#39;无名氏&#39;;
                socket_close($this->user[$k][&#39;socket&#39;]);
                $socket_key = array_search($this->user[$k][&#39;socket&#39;], $this->socket_list);
                unset($this->socket_list[$socket_key]);
                unset($this->user[$k]);
        
                $user = [];
                foreach ($this->user as $v) {
                    $user[] = $v[&#39;name&#39;];
                }
                $res = [
                    &#39;type&#39; => &#39;close&#39;,
                    &#39;users&#39; => $user,
                    &#39;msg&#39; => $u_name . &#39;已退出&#39;,
                    &#39;time&#39; => date(&#39;Y-m-d H:i:s&#39;)
                ];
                $this->sendAllUser($res);
            }
        
            /**
             * 获取用户索引
             *
             * @param $socket
             * @return int|string
             */
            protected function getUserIndex($socket)
            {
                foreach ($this->user as $k => $v) {
                    if ($v[&#39;socket&#39;] == $socket) {
                        return $k;
                    }
                }
            }
        
            /**
             * 握手
             * @param $k
             * @param $request
             */
            protected function handshake($k, $request)
            {
                preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $match);
                $key = base64_encode(sha1($match[1] . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;, true));
        
                $response = "HTTP/1.1 101 Switching Protocols\r\n";
                $response .= "Upgrade: websocket\r\n";
                $response .= "Connection: Upgrade\r\n";
                $response .= "Sec-WebSocket-Accept: {$key}\r\n\r\n";
                socket_write($this->user[$k][&#39;socket&#39;], $response);
                $this->user[$k][&#39;handshake&#39;] = true;
            }
        
            /**
             * 接收并处理消息
             *
             * @param $k
             * @param $msg
             */
            public function send($k, $msg)
            {
                $msg = $this->decode($msg);
                $msg = json_decode($msg, true);
        
                if (!isset($msg[&#39;type&#39;])) {
                    return;
                }
        
                switch ($msg[&#39;type&#39;]) {
                    case &#39;login&#39;: // 登录
                        $this->user[$k][&#39;name&#39;] = $msg[&#39;name&#39;] ?? &#39;无名氏&#39;;
                        $users = [];
                        foreach ($this->user as $v) {
                            $users[] = $v[&#39;name&#39;];
                        }
                        $res = [
                            &#39;type&#39; => &#39;login&#39;,
                            &#39;name&#39; => $this->user[$k][&#39;name&#39;],
                            &#39;msg&#39; => $this->user[$k][&#39;name&#39;] . &#39;: login success&#39;,
                            &#39;users&#39; => $users,
                        ];
                        $this->sendAllUser($res);
                        break;
                    case &#39;message&#39;: // 接收并发送消息
                        $res = [
                            &#39;type&#39; => &#39;message&#39;,
                            &#39;name&#39; => $this->user[$k][&#39;name&#39;] ?? &#39;无名氏&#39;,
                            &#39;msg&#39; => $msg[&#39;msg&#39;],
                            &#39;time&#39; => date(&#39;H:i:s&#39;),
                        ];
                        $this->sendAllUser($res);
                        break;
                }
            }
        
            /**
             * 发送给所有人
             *
             */
            protected function sendAllUser($msg)
            {
                if (is_array($msg)) {
                    $msg = json_encode($msg);
                }
        
                $msg = $this->encode($msg);
        
                foreach ($this->user as $k => $v) {
                    socket_write($v[&#39;socket&#39;], $msg, strlen($msg));
                }
            }
        
            /**
             * 解码
             *
             * @param $buffer
             * @return string
             */
            protected function decode($buffer)
            {
                $len = \ord($buffer[1]) & 127;
                if ($len === 126) {
                    $masks = \substr($buffer, 4, 4);
                    $data = \substr($buffer, 8);
                } else {
                    if ($len === 127) {
                        $masks = \substr($buffer, 10, 4);
                        $data = \substr($buffer, 14);
                    } else {
                        $masks = \substr($buffer, 2, 4);
                        $data = \substr($buffer, 6);
                    }
                }
                $dataLength = \strlen($data);
                $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
                return $data ^ $masks;
            }
        
            protected function encode($buffer)
            {
                if (!is_scalar($buffer)) {
                    throw new \Exception("You can&#39;t send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
                }
                $len = \strlen($buffer);
        
                $first_byte = "\x81";
        
                if ($len <= 125) {
                    $encode_buffer = $first_byte . \chr($len) . $buffer;
                } else {
                    if ($len <= 65535) {
                        $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
                    } else {
                        $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
                    }
                }
        
                return $encode_buffer;
            }
        }
        Copier après la connexion

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

        <!doctype html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport"
                  content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>Document</title>
        </head>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
            h3 {
                display: flex;
                justify-content: center;
                margin: 30px auto;
            }
            .but-box {
                border-radius: 5px;
                display: flex;
                justify-content: center;
                align-items: center;
                margin-top: 10px;
            }
            #box {
                display: flex;
                margin: 5px auto;
                border-radius: 5px;
                border: 1px #ccc solid;
                height: 400px;
                width: 700px;
                overflow-y: auto;
                overflow-x: hidden;
                position: relative;
            }
            #msg-box {
                width: 480px;
                margin-right: 111px;
                height: 100%;
                overflow-y: auto;
                overflow-x: hidden;
            }
            #user-box {
                width: 110px;
                overflow-y: auto;
                overflow-x: hidden;
                float: left;
                border-left: 1px #ccc solid;
                height: 100%;
                background-color: #F1F1F1;
            }
            button {
                float: right;
                width: 80px;
                height: 35px;
                font-size: 18px;
            }
            input {
                width: 100%;
                height: 30px;
                padding: 2px;
                line-height: 20px;
                outline: none;
                border: solid 1px #CCC;
            }
            .but-box p {
                margin-right: 160px;
            }
        </style>
        <body>
        
        <h3 id="这是一个php-nbsp-socket实现的web聊天室">这是一个php socket实现的web聊天室</h3>
        
        <div id="box">
            <div id="msg-box"></div>
            <div id="user-box"></div>
        </div>
        
        <div>
        
            <p><textarea cols="60" rows="3" style="resize:none;pedding: 10px"    id="content"> </textarea></p>
            <button id="send">发送</button>
        </div>
        <script src="https://cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
        <script>
            let ws = new WebSocket(&#39;ws://124.222.85.67:8888&#39;);
        
            ws.onopen = function (event) {
                console.log(&#39;连接成功&#39;);
        
                var name = prompt(&#39;请输入用户名:&#39;);
        
                ws.send(JSON.stringify({
                    type: &#39;login&#39;,
                    name: name
                }));
        
                if (!name) {
                    alert(&#39;好你个坏蛋,竟然没有输入用户名&#39;);
                }
            };
            ws.onmessage = function (event) {
                let data = JSON.parse(event.data);
                console.log(data);
        
                switch (data.type) {
                    case &#39;close&#39;:
                    case &#39;login&#39;:
                        $("#user-box").html(&#39;&#39;);
                        data.users.forEach(function (item) {
                            $("#user-box").append(`<p style="color: grey;">${item}</p>`);
                        });
                        if (data.msg) {
                            $("#msg-box").append(`<p style="color: grey;">${data.msg}</p>`);
                        }
                        break;
                    case &#39;message&#39;:
                        $("#msg-box").append(`<p><span style="color: #0A89FF">${data.time}</span><span style="color: red">${data.name}</span>${data.msg}</p>`);
                        break;
                }
            };
        
            ws.onclose = function (event) {
                alert(&#39;连接关闭&#39;);
            };
        
            document.onkeydown = function (event) {
                if (event.keyCode == 13) {
                    send();
                }
            }
        
            $("#send").click(function () {
                send();
            });
        
            function send() {
                let content = $("#content").val();
                $("#content").val(&#39;&#39;);
                if (!content) {
                    return;
                }
                ws.send(JSON.stringify({
                    type: &#39;message&#39;,
                    msg: content
                }));
            }
        </script>
        </body>
        </html>
        Copier après la connexion

        [^1]:是通讯传输的一个术语。 通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合
        [^2]:  为了建立 websocket 连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)

        推荐学习:《PHP视频教程》                                            

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 apporte plusieurs nouvelles fonctionnalités, améliorations de sécurité et de performances avec une bonne quantité de dépréciations et de suppressions de fonctionnalités. Ce guide explique comment installer PHP 8.4 ou mettre à niveau vers PHP 8.4 sur Ubuntu, Debian ou leurs dérivés. Bien qu'il soit possible de compiler PHP à partir des sources, son installation à partir d'un référentiel APT comme expliqué ci-dessous est souvent plus rapide et plus sécurisée car ces référentiels fourniront les dernières corrections de bogues et mises à jour de sécurité à l'avenir.

7 fonctions PHP que je regrette de ne pas connaître auparavant 7 fonctions PHP que je regrette de ne pas connaître auparavant Nov 13, 2024 am 09:42 AM

Si vous êtes un développeur PHP expérimenté, vous aurez peut-être le sentiment d'y être déjà allé et de l'avoir déjà fait. Vous avez développé un nombre important d'applications, débogué des millions de lignes de code et peaufiné de nombreux scripts pour réaliser des opérations.

Comment configurer Visual Studio Code (VS Code) pour le développement PHP Comment configurer Visual Studio Code (VS Code) pour le développement PHP Dec 20, 2024 am 11:31 AM

Visual Studio Code, également connu sous le nom de VS Code, est un éditeur de code source gratuit – ou environnement de développement intégré (IDE) – disponible pour tous les principaux systèmes d'exploitation. Avec une large collection d'extensions pour de nombreux langages de programmation, VS Code peut être c

Expliquez les jetons Web JSON (JWT) et leur cas d'utilisation dans les API PHP. Expliquez les jetons Web JSON (JWT) et leur cas d'utilisation dans les API PHP. Apr 05, 2025 am 12:04 AM

JWT est une norme ouverte basée sur JSON, utilisée pour transmettre en toute sécurité des informations entre les parties, principalement pour l'authentification de l'identité et l'échange d'informations. 1. JWT se compose de trois parties: en-tête, charge utile et signature. 2. Le principe de travail de JWT comprend trois étapes: la génération de JWT, la vérification de la charge utile JWT et l'analyse. 3. Lorsque vous utilisez JWT pour l'authentification en PHP, JWT peut être généré et vérifié, et les informations sur le rôle et l'autorisation des utilisateurs peuvent être incluses dans l'utilisation avancée. 4. Les erreurs courantes incluent une défaillance de vérification de signature, l'expiration des jetons et la charge utile surdimensionnée. Les compétences de débogage incluent l'utilisation des outils de débogage et de l'exploitation forestière. 5. L'optimisation des performances et les meilleures pratiques incluent l'utilisation des algorithmes de signature appropriés, la définition des périodes de validité raisonnablement,

Comment analysez-vous et traitez-vous HTML / XML dans PHP? Comment analysez-vous et traitez-vous HTML / XML dans PHP? Feb 07, 2025 am 11:57 AM

Ce tutoriel montre comment traiter efficacement les documents XML à l'aide de PHP. XML (Language de balisage extensible) est un langage de balisage basé sur le texte polyvalent conçu à la fois pour la lisibilité humaine et l'analyse de la machine. Il est couramment utilisé pour le stockage de données et

Programme PHP pour compter les voyelles dans une chaîne Programme PHP pour compter les voyelles dans une chaîne Feb 07, 2025 pm 12:12 PM

Une chaîne est une séquence de caractères, y compris des lettres, des nombres et des symboles. Ce tutoriel apprendra à calculer le nombre de voyelles dans une chaîne donnée en PHP en utilisant différentes méthodes. Les voyelles en anglais sont a, e, i, o, u, et elles peuvent être en majuscules ou en minuscules. Qu'est-ce qu'une voyelle? Les voyelles sont des caractères alphabétiques qui représentent une prononciation spécifique. Il y a cinq voyelles en anglais, y compris les majuscules et les minuscules: a, e, i, o, u Exemple 1 Entrée: String = "TutorialSpoint" Sortie: 6 expliquer Les voyelles dans la chaîne "TutorialSpoint" sont u, o, i, a, o, i. Il y a 6 yuans au total

Expliquez la liaison statique tardive en PHP (statique: :). Expliquez la liaison statique tardive en PHP (statique: :). Apr 03, 2025 am 12:04 AM

Liaison statique (statique: :) ​​implémente la liaison statique tardive (LSB) dans PHP, permettant à des classes d'appel d'être référencées dans des contextes statiques plutôt que de définir des classes. 1) Le processus d'analyse est effectué au moment de l'exécution, 2) Recherchez la classe d'appel dans la relation de succession, 3) il peut apporter des frais généraux de performance.

Quelles sont les méthodes PHP Magic (__construct, __ destruct, __ call, __get, __set, etc.) et fournir des cas d'utilisation? Quelles sont les méthodes PHP Magic (__construct, __ destruct, __ call, __get, __set, etc.) et fournir des cas d'utilisation? Apr 03, 2025 am 12:03 AM

Quelles sont les méthodes magiques de PHP? Les méthodes magiques de PHP incluent: 1. \ _ \ _ Construct, utilisé pour initialiser les objets; 2. \ _ \ _ Destruct, utilisé pour nettoyer les ressources; 3. \ _ \ _ Appel, gérer les appels de méthode inexistants; 4. \ _ \ _ GET, Implémentez l'accès à l'attribut dynamique; 5. \ _ \ _ SET, Implémentez les paramètres d'attribut dynamique. Ces méthodes sont automatiquement appelées dans certaines situations, améliorant la flexibilité et l'efficacité du code.

See all articles