Hi I have a running socket server written with PHP.
The server is listening for connections.. any idea how my client(written in javascript) is going to connect to the server and send data to it?
PS: I only know how to connect a php client to the socket server but unsure how to connect a javascript client.
Thanks all for your time.
I use standard WebSocket API for client.
And core PHP socket for server side.
know, send and received data use a header on the browser with websocket. But the code PHP socket, send and received without header and just send plain data.
So we need to simulate header on the socketing server side.
For learning and know how do it, I write this clear sample code, With this code you can send a phrase to server and receive reverse phrase that in client.
server.php
<?php
//Code by: Nabi KAZ <www.nabi.ir>
// set some variables
$host = "127.0.0.1";
$port = 5353;
// don't timeout!
set_time_limit(0);
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0)or die("Could not create socket\n");
// bind socket to port
$result = socket_bind($socket, $host, $port)or die("Could not bind to socket\n");
// start listening for connections
$result = socket_listen($socket, 20)or die("Could not set up socket listener\n");
$flag_handshake = false;
$client = null;
do {
if (!$client) {
// accept incoming connections
// client another socket to handle communication
$client = socket_accept($socket)or die("Could not accept incoming connection\n");
}
$bytes = #socket_recv($client, $data, 2048, 0);
if ($flag_handshake == false) {
if ((int)$bytes == 0)
continue;
//print("Handshaking headers from client: ".$data."\n");
if (handshake($client, $data, $socket)) {
$flag_handshake = true;
}
}
elseif($flag_handshake == true) {
if ($data != "") {
$decoded_data = unmask($data);
print("< ".$decoded_data."\n");
$response = strrev($decoded_data);
socket_write($client, encode($response));
print("> ".$response."\n");
socket_close($client);
$client = null;
$flag_handshake = false;
}
}
} while (true);
// close sockets
socket_close($client);
socket_close($socket);
function handshake($client, $headers, $socket) {
if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
$version = $match[1];
else {
print("The client doesn't support WebSocket");
return false;
}
if ($version == 13) {
// Extract header variables
if (preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if (preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$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";
socket_write($client, $upgrade);
return true;
} else {
print("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
function unmask($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
function encode($text) {
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
client.htm
<html>
<script>
//Code by: Nabi KAZ <www.nabi.ir>
var socket = new WebSocket('ws://localhost:5353');
// Open the socket
socket.onopen = function(event) {
var msg = 'I am the client.';
console.log('> ' + msg);
// Send an initial message
socket.send(msg);
// Listen for messages
socket.onmessage = function(event) {
console.log('< ' + event.data);
};
// Listen for socket closes
socket.onclose = function(event) {
console.log('Client notified socket has closed', event);
};
// To close the socket....
//socket.close()
};
</script>
<body>
<p>Please check the console log of your browser.</p>
</body>
</html>
Manual: first run php server.php on CLI and then open http://localhost/client.htm on browser.
You can see result:
http://localhost/client.htm
> I am the client.
< .tneilc eht ma I
php server.php
< I am the client.
> .tneilc eht ma I
Be careful it's just a sample code for test send and receive data, And it is not useful for executive work.
I suggest you use these projects:
https://github.com/ghedipunk/PHP-Websockets
https://github.com/esromneb/phpwebsocket
https://github.com/acbrandao/PHP/tree/master/ws
https://github.com/srchea/PHP-Push-WebSocket/
http://socketo.me/
And also I suggest you these articles for more details:
http://www.abrandao.com/2013/06/websockets-html5-php/
http://cuelogic.com/blog/php-and-html5-websocket-server-and-client-communication/
http://srchea.com/build-a-real-time-application-using-html5-websockets
Answering an old question in case people find it as I did via Google.
Nowadays nearly all contemporary browsers support the WebSocket Javascript API. Via WS it's possible for client JS in the browser to open full duplex sockets to severs written in PHP or other languages. The server must implement the WS protocol, but there are WS libraries now for PHP, Java, and other languages.
At this moment of writing, WS implementations still seem like a bit of a moving target, but, I'm currently working with WS/JS browser clients communicating with a WS/Java server and it does seem to be working.
Suggest Googling for WS implementations in your server language of choice.
Hope this helps!
I'm not aware of anything that provides arbitrary socket capabilities for JS. There is limited support for Web Sockets (which I think will require you to modify the server to conform to the space). Failing that, simple XHR might meet your needs (which would require that you modify the server to act as a web service). If the service runs on a different origin to the page, then you will need to use CORS or use a work around such as JSONP.
Try this:
http://code.google.com/p/phpwebsocket/
Shortly saying - you can't do that - it would be a security breach to let client side code open socket connections.
However, you could simulate that - send your data to another PHP page as an AJAX request, then make that PHP page communicate through the socket.
Update 2017:
In the mean time, websockets became a thing. Please note that the websocket protocol is a different thing than generic networking sockets
Related
I using PHP SocketIO class to connect NodeJS application and send messages.
Everything worked wonderfully with Socket.io 2 but after upgrade to version 3 the PHP integration is stopped working.
When I send request I am getting this response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hNcappwZIQEbMz7ZGWS71lNcROc=
But I don't see anything on NodeJS side, even when I tried to log any connection to the server by using "connection" event.
This is the PHP class:
class SocketIO
{
/**
* #param null $host - $host of socket server
* #param null $port - port of socket server
* #param string $action - action to execute in sockt server
* #param null $data - message to socket server
* #param string $address - addres of socket.io on socket server
* #param string $transport - transport type
* #return bool
*/
public function send($host = null, $port = null, $action= "message", $data = null, $address = "/socket.io/?EIO=2", $transport = 'websocket')
{
$fd = fsockopen($host, $port, $errno, $errstr);
if (!$fd) {
return false;
} //Can't connect tot server
$key = $this->generateKey();
$out = "GET $address&transport=$transport HTTP/1.1\r\n";
$out.= "Host: https://$host:$port\r\n";
$out.= "Upgrade: WebSocket\r\n";
$out.= "Connection: Upgrade\r\n";
$out.= "Sec-WebSocket-Key: $key\r\n";
$out.= "Sec-WebSocket-Version: 13\r\n";
$out.= "Origin: https://$host\r\n\r\n";
fwrite($fd, $out);
// 101 switching protocols, see if echoes key
$result= fread($fd,10000);
preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $result, $matches);
$keyAccept = trim($matches[1]);
$expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$handshaked = ($keyAccept === $expectedResonse) ? true : false;
if ($handshaked){
fwrite($fd, $this->hybi10Encode('42["' . $action . '", "' . addslashes($data) . '"]'));
fread($fd,1000000);
return true;
} else {return false;}
}
private function generateKey($length = 16)
{
$c = 0;
$tmp = '';
while ($c++ * 16 < $length) { $tmp .= md5(mt_rand(), true); }
return base64_encode(substr($tmp, 0, $length));
}
private function hybi10Encode($payload, $type = 'text', $masked = true)
{
$frameHead = array();
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
$frameHead[0] = 129;
break;
case 'close':
$frameHead[0] = 136;
break;
case 'ping':
$frameHead[0] = 137;
break;
case 'pong':
$frameHead[0] = 138;
break;
}
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
if ($frameHead[2] > 127) {
$this->close(1004);
return false;
}
} elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
}
Thank you for help!
I was having the same problem with all the libraries that exists on github, the problem is that they are abandoned or not updated to socket.io V3.
In socket.io documentation says:
TL;DR: due to several breaking changes, a v2 client will not be able to connect to a v3 server (and vice versa)
To solve this problem, you need to learn how socket.io client works, this is easy because is in the protocol documentation, in the sample-session section.
Socket.Io protocol documentation
To solve this, you will need to forget the fsockopen and fwrite functions, you need to use CURL directly doing the requests mentioned in the protocol documentation.
Request n°1
GET
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H
Open packet: Open the connection between php and socket.io server. The server will return a "session id" named "sid", you will be adding this to the url query for the subsecuent queries.
Request n°2
POST
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
post body: '40'
Namespace connection request: You need to send in the body the number 40, as a string, this means that you want to connect to socket.io "message" type
Request n°3
GET
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
Namespace connection approval : This will return if the connection is successful or if there is an error, here is when the socket.io server authorizes your connection if you need a token.
Request n°4
POST
url: /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
post body: 42[event,data]
For example 42["notifications","Hi, Im a notification"] and is equivalent to socket.emit(event,data)
Emit message to server: Send your message to the socket.io server.
Here is a BASIC example using Symfony 5.2 and HttpClientInterface:
<?php
// install dependencies before: composer require symfony/http-client
use Symfony\Component\HttpClient\CurlHttpClient;
include('vendor/autoload.php');
$client = new CurlHttpClient();
sendToSocket($client);
function sendToSocket(HttpClientInterface $client)
{
$first = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=N8hyd6w');
$res = ltrim($first->getContent(), '0');
$res = json_decode($res, true);
$sid = $res['sid'];
$second = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '40'
]);
$third = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid);
$fourth = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '42["notifications","Hi, Im a notification"]'
]);
}
As you can see, is very easy, and you dont need the troubling "copy-pasted" libraries out there. I said "copy-pasted" because all use the same code to open de socket and send the information, but no one is compatible with socket.io V3.
Here is an image, proving that the given code works as January 4 2021 with php 7.4, symfony 5.2 and socket.io V3.
This is my test server in node
// Install dependencies before: npm i express socket.io
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.on('connection', function (socket) {
console.log("New Connection with transport", socket.conn.transport.name);
socket.on('notifications', function (data) {
console.log(data);
});
});
http.listen(3000, () => {
console.log('Server started port 3000');
});
I need to say that this solution works excellent if you want to send "one direction" messages to your socket.io server, like a new notification or whatever that doesn't need a permanent connection, is just "one shot" and nothing else.
Happy coding and greetings from Mexico.
Here is another example:
First column is Postman making a request to the php server, simulating a server side event, like a new question created. In the response are the dumps of the response body from the 4 requests that you need to make.
Second column is the socket.IO node server running on port 3000
And the last column is the chrome console, simulating a user connected to the socket.IO server via websocket looking for notifications in 'questions' event.
I was wondering if anyone has had any experience with this before. I'm trying to write a simple script that will continously read data from the TCP/IP stream but for some reason or another the script reads in a bunch of data, writes it out and then just stops.
$fp = fsockopen("xxxx", 3000, $errno, $errstr, 5);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
while (!feof($fp)) {
echo fgets($fp, 128)."\n";
fflush($fp);
}
fclose($fp);
}
I'd like it to have a constant flow to it, rather then echo out a bunch of data then wait 30 seconds and output a bunch more data. Anyone have any ideas?
---- EDIT ----
ZMQ Code
include 'zmsg.php';
$context = new ZMQContext();
$client = new ZMQSocket($context, ZMQ::SOCKET_DEALER);
// Generate printable identity for the client
$identity = sprintf ("%04X", rand(0, 0x10000));
$client->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $identity);
$client->connect("tcp://xxxx:3000");
$read = $write = array();
$poll = new ZMQPoll();
$poll->add($client, ZMQ::POLL_IN);
$request_nbr = 0;
while (true) {
// Tick once per second, pulling in arriving messages
for ($centitick = 0; $centitick < 100; $centitick++) {
$events = $poll->poll($read, $write, 1000);
$zmsg = new Zmsg($client);
if ($events) {
$zmsg->recv();
echo $zmsg->body()."\n";
//printf ("%s: %s%s", $identity, $zmsg->body(), PHP_EOL);
}
}
$zmsg = new Zmsg($client);
//$zmsg->body_fmt("request #%d", ++$request_nbr)->send();
}
Here is how you connect to a server (as a client) if your goal is ONLY to PULL data (read).
<?php
$context = new ZMQContext();
$sock = new ZMQSocket($context, ZMQ::SOCKET_PULL);
$sock->connect("tcp://ADDRESS:3000");
while (true)
{
$request = $sock->recv(); # recv is blocking by default, no need to put timers.
printf ("Received: %s;%s", $request, PHP_EOL);
}
?>
if you want to reply, you'll need to use a pair socket (ZMQ::SOCKET_PAIR), then you can use:
$sock->send("data to send");
Also, if instead of you connecting to clients, clients connects to you, use the bind method instead of connect.
EDIT: use the PUSH socket type on the other side if you use the pull here, else, use the pair socket on both sides.
I am working on a little project of mine and have built a UDP scraper that uses sockets to return data about a specific sha1 hash.
It works but is incredibly slow and wondered if any one knows how I could speed it up or improve the existing code.
The code is below;
// SCRAPE UDP
private function scrapeUDP($tracker, $hash) {
// GET TRACKER DETAILS
preg_match('%udp://([^:/]*)(?::([0-9]*))?(?:/)?%i', $tracker, $info);
// GENERATE TRANSACTION ID
$transID = mt_rand(0, 65535);
// PACKED TRANSACTION ID
$packedTransID = pack('N', $transID);
// ATTEMPT TO CREATE A SOCKET
if(!$socket = #fsockopen('udp://' . $info[1], $info[2], $errno, $errstr, 2)) {
return;
}
// SET STREAM TIMEOUT
stream_set_timeout($socket, 2);
// CONNECTION ID
$connID = "\x00\x00\x04\x17\x27\x10\x19\x80";
// BUILD CONNECTION REQUEST PACKET
$packet = $connID . pack('N', 0) . $packedTransID;
// SEND PACKET
fwrite($socket, $packet);
// CONNECTION RESPONSE
$response = fread($socket, 16);
// CHECK CONNECTION RESPONSE LENGTH
if(strlen($response) < 16) {
return;
}
// UNPACK CONNECTION RESPONSE
$returnData = unpack('Naction/NtransID', $response);
// CHECK CONNECTION RESPONSE DATA
if($returnData['action'] != 0 || $returnData['transID'] != $transID) {
return;
}
// GET CONNECTION ID
$connID = substr($response, 8, 8);
// BUILD SCRAPE PACKET
$packet = $connID . pack('N', 2) . $packedTransID . $hash;
// SEND SCRAPE PACKET
fwrite($socket, $packet);
// SCRAPE RESPONSE
$response = fread($socket, 20);
// CHECK SCRAPE RESPONSE LENGTH
if(strlen($response) < 20) {
return;
}
// UNPACK SCRAPE RESPONSE
$returnData = unpack('Naction/NtransID', $response);
// CHECK SCRAPE RESPONSE DATA
if($returnData['action'] != 2 || $returnData['transID'] != $transID) {
return;
}
// UNPACK SCRAPE INFORMATION
$returnData = unpack('Nseeders/Ncompleted/Nleechers', substr($response, 8, 12));
// RETURN TRACKER INFORMATION
return array('seeders' => $returnData['seeders'], 'leechers' => $returnData['leechers'],);
}
It is my first time I have ever created anything to do with sockets or UDP so forgive me if it is a mess!
Thanks...
You have to make parallel request using socket_select() and non-blocking sockets or forks, because you are spending a lot of time in waiting for the response. Additionally, it may be better to use low-level functions like socket_read() or similar to control connection and data transmission better.
I'm new to web sockets.. i made my first web socket and i am having problems on running it now!
here is the code of the socket
// set some variables
$host = "127.0.0.1";
$port = 1234;
// don't timeout!
set_time_limit(0);
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
if($socket){
echo "socket created .... $socket\n";
}
// bind socket to port
$result = socket_bind($socket, $host, $port) or die("Could not bind to socket\n");
if($result){
echo "socket binded ... $result\n";
}
// start listening for connections
$result = socket_listen($socket, 3) or die("Could not set up socket listener\n");
if($result){
echo "socket is now listening ... $result";
}
// accept incoming connections
// spawn another socket to handle communication
$spawn = socket_accept($socket) or die("Could not accept incoming connection\n");
if($spawn){
echo $spawn."\n";
}
// read client input
$input = socket_read($spawn, 1024) or die("Could not read input\n");
if($input){
echo $input."\n";
}
// clean up input string
$input = trim($input);
// reverse client input and send back
$output = strrev($input) . "\n";
socket_write($spawn, $output, strlen ($output)) or die("Could not write output\n");
// close sockets
socket_close($spawn);
socket_close($socket);
now how can i run this code?? i wrote on my xampp shell the following code:
php htdocs/socket/server.php -q
it displays:
socket created....Resource id #4
socket binded... 1
socket is now listening...1 Resource is #5
GET socket/server.php HTTP 1.1
upgrade: WebSocket
connection: Upgrade
Host: http://localhost
sec-WebSocket-key1: 14 53 8501 z4 5R'
sec-WebSocket-key2: S 9\ 2s63, *8460!~MO#
now how can i run it.. how can i send input to it and how can i use it with JavaScript??
i made a JavaScript code but it connect for a second and then disconnect...
here is the javascipt code:
$(document).ready(function() {
if(!("WebSocket" in window)){
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about Google Chrome?</p>').appendTo('#container');
}else{
//The user has WebSockets
connect();
function connect(){
var socket;
var host = "ws://localhost:1234/websocket_source_files/myown.php";
try{
var socket = new WebSocket(host);
message('<p class="event">Socket Status: '+socket.readyState);
socket.onopen = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
}
socket.onmessage = function(msg){
message('<p class="message">Received: '+msg.data);
}
socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
}
} catch(exception){
message('<p>Error'+exception);
}
function send(){
var text = $('#text').val();
if(text==""){
message('<p class="warning">Please enter a message');
return ;
}
try{
socket.send(text);
message('<p class="event">Sent: '+text)
} catch(exception){
message('<p class="warning">');
}
$('#text').val("");
}
function message(msg){
$('#chatLog').append(msg+'</p>');
}//End message()
$('#text').keypress(function(event) {
if (event.keyCode == '13') {
send();
}
});
$('#disconnect').click(function(){
socket.close();
});
}
}//End connect()
});
</script>
<title>WebSockets Client</title>
</head>
<body>
<div id="wrapper">
<div id="container">
<h1>WebSockets Client</h1>
<div id="chatLog">
</div>
<p id="examples">e.g. try 'hi', 'name', 'age', 'today'</p>
<input id="text" type="text" />
<button id="disconnect">Disconnect</button>
</div>
</div>
</body>
</html>
please help me run this code and learn web sockets.. i really need to use them with my school project.
The socket_accept-function will block (wait) until a client connects to it. That's it's standard behavior.
But the functions you execute after you've connected your socket don't block (unless you tell them to). So you'll want to tell your script to wait until it can read from the Socket.
To do so, the socket_set_block-function is used. Also, you might want to check for any possible errors using the socket_last_error-function.
Although, I think Java or C are way bedder suited for using Sockets.
Write another PHP script which would connect to it.
you are not doing the handshake propertly.
from what you posted, you are dealing with the ietf-00 implementation ( https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-00 )
this is old and deprecated, the last one seems to be ietf-10 ( https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-10 ).
a very basic description of the handshake you need can be found here: http://en.wikipedia.org/wiki/WebSockets
(you can find in there the links to the newer and official specifications).
The important part in your case is this:
The Sec-WebSocket-Key1 and Sec-WebSocket-Key2 fields and the 8 bytes
after the fields are random tokens which the server uses to construct
a 16-byte token at the end of its handshake to prove that it has read
the client's handshake.
The handshake is constructed by concatenating the numbers from the
first key, and dividing by the number of spaces. This is then repeated
for the second key. The two resulting numbers are concatenated with
each other, and with the last 8 bytes after the fields.
The final result is an MD5 sum of the concatenated string.[7] The
handshake looks like HTTP but actually isn't. It allows the server to
interpret part of the handshake request as HTTP and then switch to
WebSocket. Once established, WebSocket data frames can be sent back
and forth between the client and the server in full-duplex mode. Text
frames can be sent full-duplex, in either direction at the same time.
The data is minimally framed with just two bytes. Each frame starts
with a 0x00 byte, ends with a 0xFF byte, and contains UTF-8 data in
between. Binary frames are not supported yet in the API. WebSocket
text frames use a terminator, while binary frames use a length prefix.
Now, some code (this will accept one connection, receive a message, and then send a response, just like a very basic and raw example to show how it can be done):
// Just to log to console
function myLog($msg)
{
echo date('m/d/Y H:i:s ', time()) . $msg . "\n";
}
// This will actually read and process the key-1 and key-2 variables, doing the math for them
function getWebSocketKeyHash($key)
{
$digits = '';
$spaces = 0;
// Get digits
preg_match_all('/([0-9])/', $key, $digits);
$digits = implode('', $digits[0]);
// Count spaces
$spaces = preg_match_all("/\\s/ ", $key, $dummySpaces);
$div = (int)$digits / (int)$spaces;
myLog('key |' . $key . '|: ' . $digits . ' / ' . $spaces . ' = ' . $div);
return (int)$div;
}
// This will read one header: value from the request header
function getWebSocketHeader($buffer, &$lines, &$keys)
{
preg_match_all("/([a-zA-Z0-9\\-]*)(\\s)*:(\\s)*(.*)?\r\n/", $buffer, $headers);
$lines = explode("\r\n", $buffer);
$keys = array_combine($headers[1], $headers[4]);
}
// This is where the handshake gets done
function handshake($peer)
{
$buffer = socket_read($peer, 4096, PHP_BINARY_READ);
socket_getpeername($peer, $address, $port);
$peerName = $address . ':' . $port;
myLog('Got from: ' . $peerName . ': ' . $buffer);
getWebSocketHeader($buffer, $lines, $keys);
if (!isset($keys['Sec-WebSocket-Key1']) || !isset($keys['Sec-WebSocket-Key2'])) {
myLog('Invalid websocket handshake for: ' . $peerName);
return;
}
$key1 = getWebSocketKeyHash($keys['Sec-WebSocket-Key1']);
$key2 = getWebSocketKeyHash($keys['Sec-WebSocket-Key2']);
$code = array_pop($lines);
// Process the result from both keys and form the response header
$key = pack('N', $key1) . pack('N', $key2) . $code;
myLog('1:|' . $key1 . '|- 2:|' . $key2 . '|3:|' . $code . '|4: ' . $key);
$response = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n";
$response .= "Upgrade: WebSocket\r\n";
$response .= "Connection: Upgrade\r\n";
$response .= "Sec-WebSocket-Origin: " . trim($keys['Origin']) . "\r\n";
$response .= "Sec-WebSocket-Location: ws://" . trim($keys['Host']) . "/\r\n";
$response .= "\r\n" . md5($key, true); // this is the actual response including the hash of the result of processing both keys
myLog($response);
socket_write($peer, $response);
}
// This is where you can send a frame (delimited by 0x00 and 0xFF)
function send($peer, $message)
{
socket_write($peer, pack('c', (int)0) . utf8_encode($message) . pack('c', (int)255));
}
// This is where you receive a frame (delimited again by 0x00 and 0xFF)
function receive($peer)
{
$buffer = socket_read($peer, 4096, PHP_BINARY_READ);
if (empty($buffer)) {
myLog('Error receiving from peer');
return;
}
return substr($buffer, 1, -1);
}
// Now create a socket
$socket = socket_create_listen(1026);
$peer = socket_accept($socket);
// Do the handshake and wait for an incoming message from the client
handshake($peer);
myLog('Got ' . receive($peer));
// Respond!
send($peer, 'hi there');
socket_close($peer);
socket_close($socket);
EDIT:
this is a very basic html that works in chrome (mine at least):
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function WebSocketTest()
{
if ("WebSocket" in window)
{
// Let us open a web socket
var ws = new WebSocket("ws://host:1026");
ws.onopen = function()
{
// Web Socket is connected, send data using send()
ws.send("Message to send");
console.log('send');
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
console.log(received_msg);
var txt = document.createTextNode(received_msg);
document.getElementById('messages').appendChild(txt);
};
ws.onclose = function()
{
// websocket is closed.
console.log('close');
};
}
else
{
// The browser doesn't support WebSocket
alert("WebSocket NOT supported by your Browser!");
}
}
</script>
</head>
<body>
<div id="sse">
Run WebSocket
</div>
<div id="messages">
</div>
</body>
</html>
is there a wake on lan script using a web language preferably php that works? Also one that has some documentation on how to get it to work like what needs to be enabled on your server etc
function wol($broadcast, $mac)
{
$hwaddr = pack('H*', preg_replace('/[^0-9a-fA-F]/', '', $mac));
// Create Magic Packet
$packet = sprintf(
'%s%s',
str_repeat(chr(255), 6),
str_repeat($hwaddr, 16)
);
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock !== false) {
$options = socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, true);
if ($options !== false) {
socket_sendto($sock, $packet, strlen($packet), 0, $broadcast, 7);
socket_close($sock);
}
}
}
Should work - call it with a broadcast IP address, and a MAC address
I know this is an old questions, but it's still the first Google result, so here's what I ended up doing after a bit of research:
Prerequisites:
Linux box on the same network
Install the wakeonlan package from your system's package manager (i.e. sudo apt-get install wakeonlan)
Now the script is as easy as this:
<?php
# replace with your target MAC address
$mac = 'aa:bb:cc:11:22:33';
exec("wakeonlan $mac");
?>
Hope that helps someone.
HTML (test.htm)
<body>
Click to WOL XX:XX:XX:XX:XX:XX
</body>
PHP (test.php)
<?php
$mymac = $_REQUEST['mymac'];
wol("255.255.255.255", $mymac);
echo 'WOL sent to '.$mymac;
function wol($broadcast, $mac){
$mac_array = preg_split('#:#', $mac); //print_r($mac_array);
$hwaddr = '';
foreach($mac_array AS $octet){
$hwaddr .= chr(hexdec($octet));
}
//Magic Packet
$packet = '';
for ($i = 1; $i <= 6; $i++){
$packet .= chr(255);
}
for ($i = 1; $i <= 16; $i++){
$packet .= $hwaddr;
}
//set up socket
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock){
$options = socket_set_option($sock, 1, 6, true);
if ($options >=0){
$e = socket_sendto($sock, $packet, strlen($packet), 0, $broadcast, 7);
socket_close($sock);
}
}
} //end function wol
?>
Since the split() function was removed from PHP 7.0.0, this script uses preg_split() to be compatible with current and previous PHP versions.
Replace XX:XX:XX:XX:XX:XX in the HTML with your target MAC to test the script.
Building upon the previous answers. Had to set udp port to 9 and repeat the MAC a couple more times before it worked for me:
function wol($mac)
{
$hwaddr = pack('H*', preg_replace('/[^0-9a-fA-F]/', '', $mac));
// Create Magic Packet
$packet = sprintf(
'%s%s',
str_repeat(chr(255), 6),
str_repeat($hwaddr, 20)
);
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock !== false) {
$options = socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, true);
if ($options !== false) {
socket_sendto($sock, $packet, strlen($packet), 0, "255.255.255.255", 9);
socket_close($sock);
}
}
}
Please read if you're running PHP in Docker, otherwise disregard this.
It seems that UDP broadcast from docker isn't being routed properly (possibly only broadcasted in the container itself, not on the host).
You may try setting (CLI) --network host or (compose) network_mode: host, but for PHP this was causing issues.
You can't send UDP WoL messages directly, as the device you're trying to control is 'offline' it doesn't show up in your router's ARP table and thus the direct message can't be delivered.
I ended up running #reboot root /usr/bin/php -S 0.0.0.0:8877 -t /home/user/php and putting the wol.php file there.
As a 'more proper' solution you may run an 'wol proxy' docker container that does exactly that (but then in a network host privileged docker container).