I have a simple PHP websocket server
Here is the full code : https://gist.github.com/hack4mer/e40094001d16c75fe5ae8347ebffccb7
while (true) {
$changed = $clients;
socket_select($changed, $null, $null, 0, 10);
//check for new socket
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket); //accpet new socket
$clients[] = $socket_new; //add socket to client array
//THIS DOES NOT WORK
print_r($_SERVER);
die();
}
In the browser's network tab, I can confirm the following request:
Request URL: ws://localhost:12345/
Provisional headers are shown
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,hi;q=0.8,ms;q=0.7
Cache-Control: no-cache
Connection: Upgrade
Host: localhost:12345
Origin: http://localhost
However am not able to access these request headers in my script.
My aim is to restrict access of the WebSocket to only few hosts
You will have to buffer the data, at least until the headers are done (CRLF).
Have a look at react/socket.
It is very easy to use non-blocking sockets with that.
To implement the websocket protocol, have a look at ratchetphp/RFC6455.
I solved this issue by doing the header checking before the handshaking.
Full code : https://gist.github.com/hack4mer/e40094001d16c75fe5ae8347ebffccb7
function perform_handshaking($receved_header,$client_conn, $host, $port)
{
$headers = array();
$lines = preg_split("/\r\n/", $receved_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
//HEADERS AVAILABLE HERE -> $headers
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//hand shaking header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host\r\n" .
"WebSocket-Location: ws://$host:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_conn,$upgrade,strlen($upgrade));
}
Related
I am trying to make a simple PHP, javascript websocket test project and I ran into the issue where PHP would not continue the while(true) loop once I call the socket accept function. It waits for an actual connection before continuing the loop. Is there a way to call this asynchronously so the while loop can continue? Or is there a way to immediately accept more incoming connections after one is accepted?
Here is my code
<?php
error_reporting(E_ALL);
/* Allow the script to hang around waiting for connections.
set_time_limit(0);
/* Turn on implicit output flushing so we see what we're getting
as it comes in.
ob_implicit_flush();*/
$address = 'localhost';
$port = 8080;
$clients = [];
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, $address, $port);
socket_listen($server);
/*$client = acceptConn($server);
$clients[0] = $client;*/
// Send WebSocket handshake headers.
$newclient = socket_accept($server);
handshake($server, $newclient);
$clients[count($clients)] = $newclient;
function handshake($server, $client){
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
'H*',
sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= "Sec-WebSocket-Version: 13\r\n";
$headers .= "Sec-WebSocket-Accept: $key\r\n\r\n";
socket_write($client, $headers, strlen($headers));
}
// Send messages into WebSocket in a loop.
do {
sleep(1);
//echo "next";
$content = 'Now: ' . time();
$response = chr(129) . chr(strlen($content)) . $content;
foreach($clients as $key => $item){
if(socket_write($item, $response) != 17){
socket_close($item);
unset($clients[$key]);
continue;
//echo json_encode($clients);
}
continue;
}
/*echo count($clients);
$newclient = socket_accept($server);
echo gettype($newclient);
$clients[count($clients)] = $client;*/
}while (true);
?>
I found the solution.
To anyone who was wondering what it was, use:
socket_set_nonblock($socketservervariable);
to make the server not wait for a client to connect before proceeding. That creates a new error though. When doing the client handshake the socket_read wanted a socket but it got the boolean value of false when the socket_accept function didn't detect a client connecting. So this was my solution to that:
$newclient = socket_accept($server);
if($newclient != false){
handshake($server, $newclient);
$clients[count($clients)] = $newclient;
echo "\n\nNew client connected!\n\n";
}
and that solved all of my problems.
I developed a WebSocket server using PHP and it worked fine with ws://, but in production environment it uses https://, then I must use wss://.
Should I use certificate to start the socket or something like that? I can't parse the headers to complete the handshake.
How can I perform handshake behind a https server?
This is a AWS EC2 machine with Amazon Certificate.
I have tried import .pem file to socket initialization, run ws:// behind my https:// environment, and nothing worked :(
Socket initialization:
$socket = stream_socket_server(
"tcp://0.0.0.0:" . env("APP_WSS_PORTA"),
$errno,
$errstr
);
I have tried also:
use Aws\Acm\AcmClient;
$cert = (new AcmClient(include config_path('aws.php')))->GetCertificate([
"CertificateArn" => "arn:aws:acm:sa-east-1:EDITED_TO_STACKOVERFLOW"
])["CertificateChain"];
$cert_path = "cert.pem";
file_put_contents(base_path($cert_path), $cert);
$context = stream_context_create(
["ssl" => ["local_cert"=> $cert_path]]
);
$socket = stream_socket_server(
"tcp://0.0.0.0:" . env("APP_WSS_PORTA"),
$errno,
$errstr,
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
$context
);
My handshake function:
function wsHandshake($data)
{
echo "> Handshake " . remoteIp() . PHP_EOL;
$lines = preg_split("/\r\n/", $data);
$headers = array();
foreach ($lines as $line) {
$line = chop($line);
if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
$headers[$matches[1]] = $matches[2];
}
}
var_dump($data); // to debug it :)
if (!isset($headers['Sec-WebSocket-Version']) || $headers['Sec-WebSocket-Version'] < 6) {
echo '> Versao do WebSocket nao suportada' . PHP_EOL;
return false;
}
$sec_accept = base64_encode(pack('H*', sha1($headers['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$response = "HTTP/1.1 101 Switching Protocols\r\n";
$response .= "Upgrade: websocket\r\n";
$response .= "Connection: Upgrade\r\n";
$response .= "Sec-WebSocket-Accept: " . $sec_accept . "\r\n";
$response .= "\r\n";
return $response;
}
var_dump with ws://
string(448) "GET / HTTP/1.1
Host: 127.0.0.1:3131
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://localhost
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: PuIYHJZ4x8IyXajFf4WAsw==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
"
var_dump with wss://
string(517) "\000\000��}hh�հ�h����`�ݘ����O��GQ�E� S�8�#��,��=��c���C8�ǯ�G!6{<\000$�+�/̨̩�,�0�
� ��\0003\0009\000/\0005\000
\000�\000\000\000�\000\000\000
\000\000
\000\000\000\000\000\000
\000\000\000#\000\000\000\000\000
hhttp/1.1\000\000\000\000\000\000\0003\000k\000i\000\000 ��"�c��GLGX�Ƶ��:�"ŵ�)բ
E��)\000\000Al�d��#Q{��t��q>��eb���u�+�d��M�!2�-��tI����z�y�\ĉ�\000\\000-\000\000\000#\000\0"...
I am trying to send data to PHP websocket server, although it sends the data but the data received is a garbage values. How can fix this to get correct values posted to websocket php server?
Below is my websocket php client script
<?php
$host = 'example.com:9000/server.php'; //where is the websocket server
$port = 9000; //ssl
$local = "http://localhost/"; //url where this script run
$data = json_encode(array("server_msg"=> "1","device_id"=> "DDD-123455678")); //data to be send
$head = "GET / HTTP/1.1"."\r\n".
"Host: $host"."\r\n".
"Upgrade: websocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
"Sec-WebSocket-Version: 13"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
////WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff"
$retdata = trim($wsdata,"\x00\xff"); //extracts data
////WebSocket handshake
fclose($sock);
echo $retdata;
?>
Thanks
Hi,
I have already tried it and it gives me error as below:
Fatal error: Uncaught exception 'WebSocket\ConnectionException' with message 'Connection to 'ws://************/server.php' in /var/www/webclientphp/vendor/textalk/websocket/lib/Client.php on line 149
WebSocket\ConnectionException: Connection to 'ws://************/server.php' failed: Server sent invalid upgrade response: HTTP/1.1 101 Web Socket Protocol Handshake Upgrade: websocket Connection: Upgrade WebSocket-Origin: ************ WebSocket-Location: ws://************:9000/demo/shout.php Sec-WebSocket-Accept:Kfh9QIsMVZcl6xEPYxPHzW8SZ8w= in /var/www/webclientphp/vendor/textalk/websocket/lib/Client.php on line 149
Please help
Your data needs to be encoded to match the Websocket protocol (frames, headers, encryption etc).
The server will be expecting websocket frames, and will try to decode them as per the protocol, so you can't just send raw data. It will also send data to you in this format.
The easiest way is to use a library, like this one
<?php
$host = 'localhost'; //where is the websocket server
$port = 8080; //ssl
$local = "http://localhost/php-websockets-master/"; //url where this script run
$data = '{"id": 2,"command": "server_info"}'; //data to be send
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
////WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
sleep(1);
fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff"
$retdata = trim($wsdata,"\x00\xff"); //extracts data
////WebSocket handshake
fclose($sock);
echo $headers;
echo $retdata;
?>
is the code I am using and the server side just echoes the string sent to it.
I have tested server side code written in php and it works fine with javascript client side code. When the above mentioned code is ran, it prints only header and that is "HTTP/1.1 426 Upgrade Required Sec-WebSocketVersion: 13". Any help explaining it would be great. Please don't suggest using Ratchet or React php. If You can suggest some library that does not need to be installed then that too would be of great help.
I have a working websocket Server (python + Tornado) which accepts Connections on port 8973. I can connect by a easy JavaScript / jquery instruction like:
ws = new WebSocket("ws://192.168.41.170:8973/rt");
But I need my php script to connect to this websocket server and send a message. I tried all most available solutions like
https://github.com/lemmingzshadow/php-websocket/
$host = '192.168.41.170'; //where is the websocket server
$port = 8973;
$local = "http://192.168.41.2/"; //url where this script run
$data = 'hello world!'; //data to be send
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
//WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff"
fclose($sock);
But this all dont work. Has anybody a working code snippet? I dont need a php-websocket server! Thanks
I think your problem is in how you are packing the data. Without knowing which protocol you are trying to use, I can't tell you how to pack it! But, there is a working code snippet in one answer to this question.