Go: PHP using websocket example

Source: Internet
Author: User
Tags key string sha1

Originally from: http://www.jb51.net/article/48019.htm

This article mainly introduces PHP using the WebSocket example, the need for friends can refer to the following

Here I draw a diagram showing the handshake part of the WebSocket connection between the client and the server, which can be done very easily in node, because the net module provided by node has already encapsulated the socket socket, and developers only need to use Consider the interaction of the data without having to deal with the establishment of the connection. and PHP does not, from the socket connection, establishment, binding, monitoring, and so on, these need our own to operate, so it is necessary to come out and say.



① and ② are actually an HTTP request and response, but we're dealing with a string that's not parsed. Such as:

Copy CodeThe code is as follows:
Get/chat http/1.1
Host:server.example.com
Origin:http://www.jb51.com

The request we always see is this, and when this thing is on the server side, we can get the information directly from some code base.

First, the PHP processing WebSocket

WebSocket connections are initiated by the client, so everything starts from the client. The first step is to parse the Sec-websocket-key string that gets sent to the client.

Copy CodeThe 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

Format of Client request

First, PHP establishes a socket connection to listen for information on the port.

1. Setting up the socket connection

On the establishment of socket sockets, I believe many universities have built computer network of people know, the following is a link to establish the process:

Copy CodeThe 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 to node, this place is too cumbersome to handle, the above lines of code are not connected, but the code is to create a socket socket must write something. Because of the complexity of the process, I wrote all sorts of processing into a class that was easy to manage and invoke.

Copy CodeThe 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; Judging 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 the message if the handshake is automatically selected by the host
$write = NULL;
$except = NULL;
Socket_select ($this->sockets, $write, $except, NULL);

foreach ($this->sockets as $socket) {
Client connecting the Host
if ($socket = = $this->master) {
$client = socket_accept ($this->master);
if ($client < 0) {
Debug
echo "Socket_accept () failed";
Continue
} else {
Connect ($client);
Array_push ($this->sockets, $client);
echo "Connect client\n";
}
} else {
$bytes = @socket_recv ($socket, $buffer, 2048,0);
if ($bytes = = 0) return;
if (! $this->handshake) {
If there is no handshake, shake first
Dohandshake ($socket, $buffer);
echo "shakehands\n";
} else {
If you have already shook hands, accept the data directly and handle
$buffer = decode ($buffer);
Process ($socket, $buffer);
echo "Send file\n";
}
}
}
}
}
}

The above code is through my debugging, not too much problem, if you want to test, you can type php/path/to/demo.php on the cmd command line; Of course, it's just a class, and if you want to test it, you have to create a new instance.

Copy CodeThe code is as follows:
$ws = new ws (' localhost ', 4000);

The client code can be a little simpler:

Copy CodeThe code is as follows:
var ws = new WebSocket ("ws://localhost:4000");
Ws.onopen = function () {
Console.log ("handshake success");
};
Ws.onerror = function () {
Console.log ("error");
};

Run the server code, when the client connects, we can see:

2. Extracting Sec-websocket-key Information

Copy CodeThe code is as follows:
function GetKey ($req) {
$key = null;
if (Preg_match ("/sec-websocket-key: (. *) \r\n/", $req, $match)) {
$key = $match [1];
}
return $key;
}

Here is relatively simple, direct regular matching, websocket information header must contain Sec-websocket-key, so we match up also relatively fast ~

3. Encrypt Sec-websocket-key

Copy CodeThe 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 again. If the encryption algorithm is wrong, the client will make a direct error when the test is performed:

4. Answer Sec-websocket-accept

Copy CodeThe code is as follows:
function Dohandshake ($socket, $req) {
Get encryption Key
$acceptKey = $this->encry ($req);
$upgrade = "http/1.1 101 switching protocols\r\n".
"Upgrade:websocket\r\n".
"Connection:upgrade\r\n".
"Sec-websocket-accept:". $acceptKey. "\ r \ n".
"\ r \ n";

Write socket
Socket_write (socket, $upgrade. chr (0), strlen ($upgrade. chr (0)));
The token handshake has succeeded and the next time the data is accepted in a data frame format
$this->handshake = true;
}

It is important to note that each request and the corresponding format, finally there is a blank line, that is, \ r \ n, the beginning of the test to lose this thing, tangled for a half day.

When the client successfully checks key, the OnOpen function is triggered:

5. Data frame Processing

Copy CodeThe code is as follows:
Parsing data Frames
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 < strlen ($data); $index + +) {
$decoded. = $data [$index] ^ $masks [$index% 4];
}
return $decoded;
}

The coding problems involved here have been mentioned in the previous article, here is not to mention that PHP on character processing function too much, but also remember not particularly clear, here there is no detailed introduction of the decoding program, directly send the data sent back to the client, can be considered a chat room mode bar.

Copy CodeThe code is as follows:
return frame Information Processing
function frame ($s) {
$a = Str_split ($s, 125);
if (count ($a) = = 1) {
Return "\X81". Chr (strlen ($a [0]). $a [0];
}
$ns = "";
foreach ($a as $o) {
$ns. = "\X81". Chr (strlen ($o)). $o;
}
return $ns;
}

Return data
function Send ($client, $msg) {
$msg = $this->frame ($msg);
Socket_write ($client, $msg, strlen ($msg));
}

Client code:

Copy CodeThe code is as follows:
var ws = new WebSocket ("ws://localhost:4000");
Ws.onopen = function () {
Console.log ("handshake success");
};
Ws.onmessage = function (e) {
Console.log ("message:" + e.data);
};
Ws.onerror = function () {
Console.log ("error");
};
Ws.send ("Li Jing");

After connectivity, the server returns the data:

Second, attention issues

1. WebSocket version Issue

The client has sec-websocket-version:13 in the handshake request, such as the version ID, this is an upgrade version, now the browser is using this version. While the previous version is more cumbersome in the Data encryption section, it sends two keys:

Copy CodeThe 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 this is the version (older, no longer in use), you need to get it in the following way

Copy CodeThe code is as follows:
function Encry ($key 1, $key 2, $l 8b) {//get The numbers Preg_match_all ('/([\d]+)/', $key 1, $key 1_num); Preg_match_all ('/([ \d]+)/', $key 2, $key 2_num);

$key 1_num = implode ($key 1_num[0]);
$key 2_num = implode ($key 2_num[0]);
Count spaces
Preg_match_all ('/([]+)/', $key 1, $key 1_SPC);
Preg_match_all ('/([]+)/', $key 2, $key 2_SPC);

if ($key 1_spc==0| $key 2_spc==0) {$this->log ("Invalid key"); return;}
Some Math
$key 1_sec = Pack ("N", $key 1_num/$key 1_spc);
$key 2_sec = Pack ("N", $key 2_num/$key 2_spc);

return MD5 ($key 1_sec. $key 2_sec. $l 8b,1);
}

Only Infinite spit groove this verification way! WebSocket mode of operation compared to NodeJs:

Copy CodeThe code is as follows:
Server programs
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 protocols\r\n ');
O.write (' upgrade:websocket\r\n ');
O.write (' connection:upgrade\r\n ');
O.write (' sec-websocket-accept: ' + key + ' \ r \ n ');
O.write (' \ r \ n ');
}else{
Console.log (e);
};
});
}). Listen (8000);

2. Data Frame Parsing code

This article does not give decodeframe such data frame parsing code, the previous text gives the format of the data frame, the analysis is purely physical activity.

Go: PHP using websocket example

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.