PHP websocket and phpwebsocket for real-time web chat

Source: Internet
Author: User
Tags php websocket sha1 encryption

PHP websocket and phpwebsocket for real-time web chat

Preface

As a new feature in HTML5, websocket has been very popular because it is really cool. It breaks the conventional thinking of http "request-response" and enables the server to actively push messages to the client, this article describes how to use PHP and JS websocket to implement a webpage real-time chat room;

I have written an article about how to use ajax long polling for real-time web page chat. For details, see the link: JavaScript and jQuery for real-time web page chat for ajax long polling, however, polling and server pending are useless, and websocket is a new trend.

Recently, it took a little time to "squeeze" The websocket "request-original return" server, which was made a long time ago, and improved the client functions with js, I will share the process and ideas with you. By the way, I will also popularize websocket-related knowledge. Of course, there are many articles about websocket, And I will skip some theoretical things, provide reference articles for your choice.

Post a chat room before the body starts (do not care about the CSS slag page ):

Then the source code is of course: I am the source code link-github-pillow book

Introduction to websocket

WebSocket is not a technology, but a brand new protocol. It applies the Socket of TCP and defines a new important capability for network applications: Dual-work transmission and two-way communication between the client and the server. It is a new trend in server pushing client messages following Java applets, XMLHttpRequest, Adobe Flash, ActiveXObject, and various Comet technologies.

Relationship with http

In the network layer, both the websocket and http protocols are application-layer protocols, which are based on the tcp transmission layer. However, when websocket establishes a connection, the http 101 switch protocol is used to achieve the protocol conversion (Upgrade). The HTTP protocol is switched to the WebSocket communication protocol, which is called "handshake ";

After the handshake is successful, websocket uses its own protocol to communicate, and it has nothing to do with http.

Handshake

The following is a typical handshake http header sent by my 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 ', and then performs sha1 encryption, finally, it is converted to base64 encoding. The key is returned to the client using the "Sec-WebSocket-Accept" field. After the client matches the key, the connection is established and the handshake is completed;

Data Transmission

Websocket has its own defined data transmission format, called Frame. It is a data Frame structure, in the unit of 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, read this article The WebSocket Protocol 5. data frames feel that they are not very flexible in binary operations, so they do not challenge themselves to write algorithms to parse data. The following data frame parsing and encapsulation are all online algorithms used.

However, I still often use the data in the payment gateway during my work. This must be carefully studied and summarized. Well, write down it first.

Websocket server implemented by PHP

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

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

The following is a simple comment on the functions used in the code.

File descriptor

It may be strange to mention 'file descriptor.

However, as a server, you must store and identify connected sockets. Each socket represents a user. How to associate and query user information and the correspondence between socket is a problem. Here we apply a little bit of tips on file descriptors.

We know that linux is 'Everything is file', And the socket implementation in C language is a 'file descriptor '. This file descriptor is generally an int value that increases sequentially when files are opened, it is always 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 standard output corresponds to file descriptor 1; and the standard error corresponds to file descriptor 2; so we can use 0 1 2 pairs of input and output redirection.

PHP socket similar to C socket naturally inherits this point, and the socket it creates is also a type of resource type that has the int value of 4 5. We can use the (int) or intval () function 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 result is similar:

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

The following code creates a server socket:

// Create a TCP socket. The optional values of this function are very detailed in the official documentation. We will not mention $ this-> master = socket_create (AF_INET, SOCK_STREAM, SOL_TCP) here ); // set IP address and port reuse. You can use this port again after restarting the server. socket_set_option ($ this-> master, SOL_SOCKET, SO_REUSEADDR, 1 ); // bind the IP address and port to the server socket; socket_bind ($ this-> master, $ host, $ port); // The listen function changes the active connection set interface to the connected set interface, this enables other sockets to access the server. The subsequent parameters are the maximum number of custom sockets to be processed. When the concurrency is high, this value can be set to a larger value, although it is also subject to the constraints of the system environment. Socket_listen ($ this-> master, self: LISTEN_SOCKET_NUM );

In this way, we will get a server socket. When a client connects to this socket, it will change the state to readable. Then, let's look at the server's processing logic.

Server Logic

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

The select function uses the traditional select model. The readable, written, and abnormal sockets are placed in the $ socket, $ write, and $ response t arrays respectively, and the number of sockets whose status changes is returned, if an error occurs, the function returns false.

Note that the last two time parameters have different units and can be used together to indicate the duration of socket_select blocking. If the value is 0, this function returns immediately and can be used for polling. If it is NULL, the function will be blocked all the time. Here we set the $ TV _sec parameter to null to keep blocking until an operable socket returns.

The main logic of the server is as follows:

$ Write = $ response T = NULL; $ sockets = array_column ($ this-> sockets, 'resource'); // obtain all socket resources $ read_num = socket_select ($ sockets, $ write, $ response T, NULL); foreach ($ sockets as $ socket) {// process the connection logic if the server socket is readable; if ($ socket ==$ this-> master) {socket_accept ($ this-> master); // socket_accept () accept the connection request "listen socket (like our server socket)" and a client socket. If an error occurs, false is returned. self: connect ($ client); co Ntinue;} // if other connected sockets are readable, read the data and process the response logic} else {// function socket_recv () accept len-byte data from the socket and save it in $ buffer. $ Bytes = @ socket_recv ($ socket, $ buffer, 2048, 0); if ($ bytes <9) {// when the client is suddenly interrupted, the server will receive an 8-byte message (because of its data frame mechanism, the 8-byte message is considered as a client exception interruption message), and the server will process the offline logic, and encapsulate it as a message to broadcast $ recv_msg = $ this-> disconnect ($ socket);} else {// if the client has not yet handshakes, execute the 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. The log records and exception handling are skipped, and there are also some data frame parsing and encapsulation methods, which may not be easy to read, if you are interested, go to github to support my source code ~~

In addition, to facilitate the interaction between the server and the client, I have defined a json message format, which is similar:

$ Msg = ['type' => $ msg_type, // There are common messages, online and offline messages, server messages 'from' => $ msg_resource, // message source 'content' => $ msg_content, // message content 'user _ list' => $ uname_list, // to facilitate synchronization of current online users and names];
Create a client

The frontend uses js to call the Websocket method. It is easy to create a websocket connection. The server will complete the connection and handshake operations for us. js uses the event mechanism to process 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 receives the message event ws. onmessage = function (e) {var msg = JSON. parse (e. data);} // websocket error event ws. onerror = function (){};

Sending a message is also very simple and can be called directly.ws.send(msg)Method.

Page Functions

The page mainly makes it easy for users to use. Here, a keyboard monitoring event is added to the message box textarea. 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;    }}

You can also generate a default unique user name when you open the client;

Then there are some data parsing structures and updates to the client page. Here we will not be so embarrassed. If you are interested, you can refer to the source code.

Asynchronous User Name Processing

I have to mention a small problem when determining the user name during user login. I originally wanted to directly send the user name to the server after creating a connection on the client, however, the error message "websocket is still in connection or closed" is reported in the console.

Uncaught DOMException: Failed to execute 'send' on 'websocket ': Still in CONNECTING state.

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

Later, I suddenly thought of the single-thread blocking mechanism of js, So that I realized that sleep is useless. Using the js event mechanism is the right way: So I added the logic on the server, after the handshake is successful, the client sends a message indicating that the handshake has been successful to the client. The client first saves the user name to a global variable, receives the notification message indicating that the handshake is successful on the server, and then sends the user name, the username is updated immediately.

Summary Chat Room extension direction

The Simple chat room has been completed, and of course it should be given a bright future with hope. I hope someone can achieve it:

  • Page beautification (add color for information, etc)
  • The server recognizes the '@' character and only writes data to a socket for private chat in the chat room;
  • Multi-process (using redis and other cache databases to share resources), refer to my previous article: Exploring PHP multi-process
  • Message record database persistence (log logs are not easy to analyze)
  • ...
Summary

Reading more classic books is still very useful. Some things are really accessible, and APUE/UNP should be repeated several times. In addition, with the ever-changing Internet technology, it is quite comfortable to pick out some of your favorite things and share them with you (although it took at least 10 hours for the program to work with the blog ...).

Refer:

Websocket protocol translation

Root question HTTP and WebSocket protocols (below)

Learn how WebSocket protocol is implemented from the top layer to the bottom layer (revised version)

Well, continuous updates. If you like it, you can give me a suggestion or follow up. If you have any mistakes, please correct them. Thank you.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.