How to use PHP websocket to realize web page real-time chat

Time:2022-7-26
catalogue
  • preface
  • websocket
    • brief introduction
    • Relationship with http
    • handshake
    • data transmission
  • Implementing websocket server with PHP
    • File descriptor
    • Create server socket
    • Server logic
  • client
    • Create client
    • Page function
    • User name asynchronous processing
  • summary

    preface

    Recently, I have struggled to “squeeze out” a little time, improved the websocket “request return as is” server made a long time ago, improved the client function with JS, shared the process and ideas with you, and popularized the knowledge related to websocket by the way. Of course, there are many articles discussing websocket now, and I will skip some theoretical things and give reference articles for you to choose to read.

    Before the text starts, post a rendering of the chat room (please don’t care about the CSS page):

    websocket

    brief introduction

    Websocket is not a technology, but a new protocol. It uses TCP socket, which defines a new and important capability for network applications: Double full-service transmission and two-way communication between client and server. It is a new trend for servers to push client messages after Java applets, XMLHttpRequest, Adobe Flash, activexobject, and various comet technologies.

    Relationship with http

    In terms of network layering, websocket and HTTP protocol are both application layer protocols based on TCP transport layer, but websocket uses HTTP’s 101 switch protocol to achieve protocol upgrade when establishing a connection, switching from HTTP protocol to websocket communication protocol. This action protocol is called “handshake”;

    After the handshake is successful, websocket communicates in the way specified by its own protocol, which has nothing to do with HTTP.

    handshake

    The following is a typical handshake HTTP header sent by my own browser:

    After receiving the handshake request, the server extracts the “sec websocket key” field in the request header, recovers a fixed string ‘258eafa5-e914-47da-95ca-c5ab0dc85b11’, then encrypts it with SHA1, and finally converts it into Base64 encoding, which is returned to the client as a key in the “sec websocket accept” field. After the client matches this key, it establishes a connection and completes the handshake;

    data transmission

    Websocket has its own data transmission format, called frame. The following figure shows the structure of a data frame, in which the unit is bit:

      0                   1                   2                   3

      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

     +-+-+-+-+——-+-+————-+——————————-+

     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |

     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |

     |N|V|V|V|       |S|             |   (if payload len==126/127)   |

     | |1|2|3|       |K|             |                               |

     +-+-+-+-+——-+-+————-+ – – – – – – – – – – – – – – – +

     |     Extended payload length continued, if payload len == 127  |

     + – – – – – – – – – – – – – – – +——————————-+

     |                               |Masking-key, if MASK set to 1  |

     +——————————-+——————————-+

     | Masking-key (continued)       |          Payload Data         |

     +——————————– – – – – – – – – – – – – – – – +

     :                     Payload Data continued …                :

     + – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – +

     |                     Payload Data continued …                |

     +—————————————————————+

    What is the meaning of each field? If you are interested, you can take a look at this article the websocket protocol 5. the data frame feels that you are not very flexible in binary operation, so you don’t have to challenge yourself to write algorithms to parse data. The following data frame parsing and encapsulation are online algorithms.

    However, in my work, I write that the hexadecimal operation of data is often used in the payment gateway. This must be carefully studied and summarized. Well, write it down first.

    Implementing websocket server with PHP

    If PHP implements websocket, it mainly applies the socket function library of PHP:

    The socket function library of PHP is very similar to the socket function of C language. I’ve looked through APUE before, so I think it’s quite understandable. Read the socket function in the PHP manual. I think you can also have a certain understanding of PHP socket programming.

    The functions used will be briefly commented in the code below.

    File descriptor

    It may be strange to suddenly mention ‘file descriptor’.

    But as a server, you must store and identify the connected sockets. Each socket represents a user. How to associate and query the correspondence between user information and sockets is a problem. Here is a little skill about file descriptors.

    We know that Linux is’ everything is a file ‘, and the implementation of sockets in C language is’ file descriptors’. This file descriptor is generally an int value that increases in the order of opening files, increasing from 0 (of course, the system is limited). Each socket corresponds to a file, and the read / write socket is the file corresponding to the operation, so the read and write functions can also be applied like the file system.

    Tips: in Linux, the standard input corresponds to file descriptor 0; The file descriptor corresponding to standard output is 1; The file descriptor corresponding to the standard error is 2; So we can use 012 to redirect input and output.

    PHP socket, which is similar to C socket, naturally inherits this. The socket it creates is also a resource type with an int value of 45. We can use (int) or intval () functions to convert the socket into a unique ID, so that we can use a ‘class index array’ to store socket resources and corresponding user information;

    The results are similar:

    
    $connected_sockets = array(
        (int)$socket => array(
            'resource' => $socket,
            'name' => $name,
            'ip' => $ip,
            'port' => $port,
            ...
        )
    )

    Create server socket

    The following is a code to create a server socket:

    //Create a TCP socket. The optional values of this function are written in great detail in the official document, which will not be mentioned here
    $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    //Set IP and port reuse, and reuse this port after restarting the server;
    socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
    //Bind the IP and port to the server socket;
    socket_bind($this->master, $host, $port);
    //The listen function changes the active socket interface into the connected socket interface, so that this socket can be accessed by other sockets, so as to realize the server function. The latter parameter is the maximum number of customized sockets to be processed. In the case of high concurrency, this value can be set a little larger, although it is also constrained by the system environment.
    socket_listen($this->master, self::LISTEN_SOCKET_NUM);

    In this way, we get a server socket. When a client connects to this socket, it will change its state to readable. It depends on the processing logic of the server next.

    Server logic

    Here we will focus on socket_ select($read, $write, $except, $tv_sec [, $tv_usec]):

    The select function uses the traditional select model. Read, write, and abnormal sockets will be put into the $socket, $write, $except arrays respectively, and then return the number of sockets whose state has changed. If an error occurs, the function will return false

    It should be noted that the last two time parameters, which have different units, can be used together to represent socket_ Select the blocking duration. When it is 0, this function returns immediately, which can be used in the polling mechanism. When it is null, the function will block all the time. Here we set $tv_ The SEC parameter is null. Let it block until an operable socket returns.

    The following is the main logic of the server:

    $write = $except = NULL;
    $sockets = array_ column($this->sockets, 'resource'); //  Get all socket resources
    $read_num = socket_select($sockets, $write, $except, NULL);
    
    foreach ($sockets as $socket) {
            //If the server socket is readable, the connection logic is processed;            
            if ($socket == $this->master) {
                socket_accept($this->master);
                // socket_ Accept () accepts the connection of the request "listening socket (like our server socket)", and a client socket, which returns false in case of error;
                 self::connect($client);
                 continue;
                }
            //If you can read other connected sockets, read their data and process the response logic
            } else {
                //Function socket_ Recv() accepts len bytes of data from the socket and saves it in $buffer.
                $bytes = @socket_recv($socket, $buffer, 2048, 0);
    
                if ($bytes < 9) {
                    //When the client suddenly interrupts, the server will receive an 8-byte message (due to its data frame mechanism, we think that the 8-byte message is an abnormal interrupt message of the client), and the server will process the offline logic, package it as a message and broadcast it
                    $recv_msg = $this->disconnect($socket);
                } else {
                    //If this client has not yet handshaked, execute handshake logic
                    if (!$this->sockets[(int)$socket]['handshake']) {
                        self::handShake($socket, $buffer);
                        continue;
                    } else {
                        $recv_msg = self::parse($buffer);
                    }
                }
    
                //Broadcast message
                $this->broadcast($msg);
            }
        }
    }

    Here is just the basic code for the server to process messages. Logging and exception handling have been skipped. There are also some methods of data frame parsing and encapsulation. You may not see love. If you are interested, you can go to GitHub to support my source code~~

    In addition, in order to facilitate the interaction between the server and the client, I have defined the message format of JSON type, which is similar to:

    $msg = [
        'type' => $msg_ Type, // there are general messages, online and offline messages, and server messages
        'from' => $msg_ Resource, // message source
        'content' => $msg_ Content, // message content
        'user_ list' => $uname_ List, // it is convenient to synchronize the current number of online people and names
        ];

    client

    Create client

    At the front end, we can easily create a websocket connection by calling the websocket method with JS. The server will help us complete the connection and handshake operations. JS uses the event mechanism to handle the interaction between the browser and the server:

    //Create a websocket connection
    var ws = new WebSocket("ws://127.0.0.1:8080");
    
    //Websocket creation success event
    ws.onopen = function () {
    };
    
    //Websocket received message event
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
    }
    
    //Websocket error event
    ws.onerror = function () {
    };

    Sending messages is also very simple. Call WS directly The send (MSG) method is fine.

    Page function

    The main purpose of the page is to make it easy for users to use. Here, a keyboard monitoring event is added to the textarea message box. When the user presses the Enter key, the message is sent directly;

    
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }

    There is also a default unique user name generated when the user opens the client;

    Then there are some data parsing structures and updates to the client page. Here is no longer wordy. You can see the source code if you are interested.

    User name asynchronous processing

    Here I have to mention a small problem when determining the user name when logging in. I originally wanted to send the user name directly to the server after the client created a connection, but the console reported the error message “websocket is still connected or closed”.

    Uncaught DOMException: Failed to execute ‘send’ on ‘WebSocket’: Still in CONNECTING state.

    Considering that the connection may not be handled well, I implemented the sleep method and waited a second to send the user name, but the error still exists.

    Later, I suddenly thought of JS’ single thread blocking mechanism, and realized that it was useless to use sleep to block all the time, and it was the right way to make good use of JS’ event mechanism: so I added logic on the server side and sent a message that the handshake was successful to the client after the handshake was successful; The client first stores the user name into a global variable, and then sends the user name after receiving the reminder message of successful handshake from the server, so the user name is successfully updated at the first time.

    summary

    Chat room expansion direction

    The simple chat room has been completed. Of course, we need to give it a promising future. I hope someone can realize it

    • Page beautification (adding colors to information, etc.)
    • The server recognizes the ‘@’ character and only writes data to a socket to realize private chat in the chat room
    • Multi process (use redis and other cache databases to share resources)
    • Message recording database persistence (log log is still inconvenient for analysis)

    The above is the details of how to use PHP websocket to realize web page real-time chat. For more information about using PHP websocket to realize web page real-time chat, please pay attention to other related articles of developeppaer!

    Recommended Today

    JS generate guid method

    JS generate guid method https://blog.csdn.net/Alive_tree/article/details/87942348 Globally unique identification(GUID) is an algorithm generatedBinaryCount Reg128 bitsNumber ofidentifier , GUID is mainly used in networks or systems with multiple nodes and computers. Ideally, any computational geometry computer cluster will not generate two identical guids, and the total number of guids is2^128In theory, it is difficult to make two […]