PHP using WebSocket sample _php Tutorial

Source: Internet
Author: User
Tags key string
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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code as follows:
$ws = new ws (' localhost ', 4000);

The client code can be a little simpler:

Copy the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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 the Code code 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.

http://www.bkjia.com/PHPjc/740662.html www.bkjia.com true http://www.bkjia.com/PHPjc/740662.html techarticle below I draw a diagram showing the handshake portion of the WebSocket connection between the client and the server, which can be done very easily in node, because node provides the net module ...

  • Related Article

    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.