This article mainly introduces examples of using websocket in PHP. Friends who need it can refer to it
Below I drew a picture to demonstrate the handshake part when establishing a websocket connection between client and server. This part can be completed very easily in node, because the net module provided by node has already encapsulated the socket. Developers use At this time, you only need to consider the interaction of data without dealing with the establishment of the connection. However, PHP does not. From socket connection, establishment, binding, monitoring, etc., we need to operate these by ourselves, so it is necessary to take it out and talk about it. ① and ② are actually an HTTP request and response, but what we get during the processing is an unparsed string. like: The code is as follows: GET /chat HTTP/1.1 Host: server.example.com Origin: http://www.jb51.com The requests we usually see look like this. When this thing reaches the server, we can get this information directly through some code libraries. 1. Processing websocket in php WebSocket connections are actively initiated by the client, so everything must start from the client. The first step is to parse the Sec-WebSocket-Key string sent by the client. The code is as follows: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://www.jb51.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 client request format First, php establishes a socket connection and listens for port information. 1. Establishment of socket connection Regarding the establishment of sockets, I believe many people who have studied computer network in college know this. The following is a process of establishing a connection: The code is as follows: // Create a socket socket $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind($master, $address, $port); socket_listen($master); Compared with node, the processing of this place is really troublesome. The above lines of code do not establish a connection, but these codes are what must be written to establish a socket. Since the processing process is slightly complicated, I wrote various processes into a class to facilitate management and calling. The code is as follows: //demo.php Class WS { var $master; // client connecting to server var $sockets = array(); // Socket management in different states var $handshake = false; // Determine whether to shake hands function __construct($address, $port){ //Create a socket socket $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed"); socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed"); socket_bind($this->master, $address, $port) or die("socket_bind() failed"); socket_listen($this->master, 2) or die("socket_listen() failed"); $this->sockets[] = $this->master; // debug echo("Master socket : ".$this->master."n"); while(true) { //Automatically select the socket for incoming messages. If it is a handshake, automatically select the host. $write = NULL; $except = NULL; socket_select($this->sockets, $write, $except, NULL); foreach ($this->sockets as $socket) { //Client that connects to the host if ($socket == $this->master){ $client = socket_accept($this->master); if ($client sockets, $client); echo "connect clientn"; } else { $bytes = @socket_recv($socket,$buffer,2048,0); if (!$this->handshake) { // If you do n’t shook hands, shook hands to respond first //doHandShake($socket, $buffer); echo "shakeHandsn"; } else { to $buffer = decode($buffer); //process($socket, $buffer); echo "send filen"; } } } } } } The above code has been debugged by me, and there is no big problem. If you want to test it, you can type php /path/to/demo.php in the cmd command line; of course, the above is just a class, if you want to test it , you have to create a new instance. The code is as follows: $ws = new WS('localhost', 4000); The client code can be slightly simpler: The code is as follows: var ws = new WebSocket("ws://localhost:4000"); ws.onopen = function(){ console.log("Handshake successful"); }; ws.onerror = function(){ console.log("error"); }; Running the server code, when the client connects, we can see: 2. Extract Sec-WebSocket-Key information The code is as follows: function getKey($req) { $key = null; if (preg_match("/Sec-WebSocket-Key: (.*)rn/", $req, $match)) { $key = $match[1]; } Return $key; } It’s relatively simple here. Direct regular matching. The websocket information header must contain Sec-WebSocket-Key, so we can match it quickly~ 3. Encryption Sec-WebSocket-Key The code is as follows: function encry($req){ $key = $this->getKey($req); $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; Return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); } The SHA-1 encrypted string is then base64 encrypted. If the encryption algorithm is wrong, the client will directly report an error when checking: 4. Reply Sec-WebSocket-Accept The code is as follows: function dohandshake($socket, $req){ // Get encryption key $acceptKey = $this->encry($req); $upgrade = "HTTP/1.1 101 Switching Protocolsrn" . "Upgrade: websocketrn" . "Connection: Upgradern" . "Sec-WebSocket-Accept: " . $acceptKey . "rn" . "rn"; //Write to socket socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0))); // Mark that the handshake has been successful, and the next time the data is accepted, it will be in data frame format. $this->handshake = true; } You must pay attention here. Every request and corresponding format has a blank line at the end, which is rn. I lost this thing when I started testing, and I struggled for a long time. When the client successfully checks the key, the onopen function will be triggered: 5. Data frame processing The code is as follows: // Parse the data frame function decode($buffer) { $len = $masks = $data = $decoded = null; $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); } for ($index = 0; $index frame($msg); socket_write($client, $msg, strlen($msg)); } Client code: The code is as follows: var ws = new WebSocket("ws://localhost:4000"); ws.onopen = function(){ console.log("Handshake successful"); }; ws.onmessage = function(e){ console.log("message:" + e.data); }; ws.onerror = function(){ console.log("error"); }; ws.send("Li Jing"); After sending data after connection, the server returns as it is: 2. Pay attention to problems 1. websocket version problem The client's request during the handshake contains Sec-WebSocket-Version: 13, which is a version identifier. This is an upgraded version, and all current browsers use this version. The previous version was more troublesome in the data encryption part. It would send two keys: The code is as follows: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Origin: http://www.jb51.net Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Key1: xxxx Sec-WebSocket-Key2: xxxx If it is this version (older and no longer in use), you need to obtain it through the following method The code is as follows: function encry($key1,$key2,$l8b){ //Get the numbers preg_match_all('/([d]+)/', $key1, $key1_num); preg_match_all('/([d]+)/' , $key2, $key2_num); $key1_num = implode($key1_num[0]); $key2_num = implode($key2_num[0]); //Count spaces preg_match_all('/([ ]+)/', $key1, $key1_spc); preg_match_all('/([ ]+)/', $key2, $key2_spc); if($key1_spc==0|$key2_spc==0){ $this->log("Invalid key");return; } //Some math $key1_sec = pack("N",$key1_num / $key1_spc); $key2_sec = pack("N",$key2_num / $key2_spc); return md5($key1_sec.$key2_sec.$l8b,1); } I can only complain endlessly about this verification method! Compared to nodeJs’s websocket operation mode: The code is as follows: //server program var crypto = require('crypto'); var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o){ var key; o.on('data',function(e){ if(!key){ //shake hands key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; key = crypto.createHash('sha1').update(key + WS).digest('base64'); o.write('HTTP/1.1 101 Switching Protocolsrn'); o.write('Upgrade: websocketrn'); o.write('Connection: Upgradern'); o.write('Sec-WebSocket-Accept: ' + key + 'rn'); o.write('rn'); }else{ console.log(e); }; }); }).listen(8000); 2. Data frame parsing code This article does not provide data frame parsing code such as decodeFrame. The format of the data frame is given in the previous article. Parsing is purely physical work.