Im trying to "port" some code from PHP into Node and im struggling to find out how to get it working. Im trying too connect to an RCON server over UDP.
Here is a simplified version of the PHP version which works:
$address = '1.2.3.4:1234';
$password = 'password';
$socket = stream_socket_client('udp://' . $address, $errno, $errstr);
stream_set_blocking($socket, 0);
$loginRequest = chr(0x45) . $password;
stream_socket_sendto($socket, $loginRequest, 0, $address);
echo 'RCON Login Request Sent' . PHP_EOL;
echo $loginRequest . PHP_EOL;
echo PHP_EOL;
do {
$data = stream_socket_recvfrom($socket, 127, 0, $address);
} while (!$data);
echo 'RCON Received Response' . PHP_EOL;
echo $data . PHP_EOL;
Here is what I have got in Node, which doesnt seem to do anything. I dont even get any acknowledgment of a connection from the server.
var address = '1.2.3.4';
var port = '1234';
var password = 'password';
var socket = require('dgram').createSocket('udp4');
socket.on('message', function (msg, from) {
console.log(msg);
});
socket.on('listening', function () {
var loginPacket = new Buffer(String.fromCharCode(0xff) + password);
socket.send(loginPacket, 0, loginPacket.length, port, address, function (err, bytes) {
console.log('packet sent');
});
});
socket.on('error', function (err) {
console.log('ERROR: ', err);
});
socket.bind(0);
Any help with this would be great! maca134
The default encoding for passing a string to the Buffer constructor is not binary, so you need to pass binary as the encoding: new Buffer('\xFF' + password, 'binary'). The first 0xFF byte causes issues with the default utf8 encoding. Also, another difference is you're using 0xFF in node whereas you're using 0x45 in PHP.
socket.bind takes a port and optional host. You're supplying port 0.
I'm not sure which port you want to listen on but for sake of example I'll use 888.
socket.bind(888);
Related
I have created Laravel Lumen project which receives API requests from my front-end. The request contains a hex string. What I need to do is I have to convert a hex string to a byte array and send it through specific IP and Port with a socket connection. I don't know the structure of the socket server but I needed to connect to the socket server and send my byte array. My problem is my approach is failing.
class TwoController extends Controller
{
public function sendRequest(Request $request)
{
try {
$v = $this->validate($request, [
'hex' => 'required',
]);
$host = "xxx.xxx.xxx.xxx";
$port = 6090;
$timeout=15;
$sk = fsockopen($host, $port, $errnum, $errstr, $timeout);
if(!is_resource($sk)){
exit("connection fail: " . $errnum . " " . $errstr);
} else {
$array = hex2ByteArray($v['hex']);
fwrite($sk, $array);
echo fread($sk, 6);
fclose($sk);
}
} catch(ValidationException $e){
return response()->json($e->errors(), 422);
} catch(Exception $e){
return response($e->getMessage(), 500);
}
}
}
It is my method of converting hex2ByteArray
function hex2ByteArray($hexString) {
$string = hex2bin($hexString);
return unpack('C*', $string);
}
UPDATE 1:
Thank you to those who commented on my problem. I figured out that my client's server is not running properly. I contacted the client and told them to restart the server. Now I'm receiving responses from the socket sever. I'm not sure that I'm sending right byte of data. The response they gave me is also byte. I send the response to the serial port machine and wait for it to do the magic. If it says it is correct then it is correct. I will update you when I found out what is the right response and the right request.
I am trying to authenticate a Microsoft Teams custom Bot with PHP, following the Microsoft instructions and read de C# example code.
Microsoft Intructions steps:
1. Generate the hmac from the request body of the message. There are standard libraries on most platforms. Microsoft Teams uses standard
SHA256 HMAC cryptography. You will need to convert the body to a byte
array in UTF8.
2. To compute the hash, provide the byte array of the shared secret.
3. Convert the hash to a string using UTF8 encoding.
4. Compare the string value of the generated hash with the value provided in the HTTP request.
I had write a small php script to test this in local:
<?php
//Function to generate C# byte[] equivalent
function unpak_str($val){
$b = unpack('C*', $val);
foreach ($b as $key => $value)
$byte_a .= $value;
return $byte_a;
}
//multi test outputs
function hasher($values=[], &$output){
//my secret share
$secret="ejWiKHgsKY1ZfpJwJ+wIiN4+bgsFad/lkpu9/MWNXgM=";
//diferent test
$secret_64=base64_decode($secret);
$secret_b=unpak_str($secret);
$secret_b_64=unpak_str(base64_decode($secret));
foreach($values as $msg){
$hs = hash_hmac("sha256",$msg,$secret, true);
$hs_64 = hash_hmac("sha256",$msg,$secret_64, true);
$hs_b = hash_hmac("sha256",$msg,$secret_b, true);
$hs_b_64 = hash_hmac("sha256",$msg,$secret_b_64, true);
$output.=base64_encode($hs)." <BR>";
$output.=base64_encode($hs_64)." <BR>";
$output.=base64_encode($hs_b)." <BR>";
$output.=base64_encode($hs_b_64)." <BR>";
}
}
//Get data
$data=file_get_contents('php://input');
//real data request content for test
$data ='{type":"message","id":"1512376018086","timestamp":"2017-12-04T08:26:58.237Z","localTimestamp":"2017-12-04T09:26:58.237+01:00","serviceUrl":"https://smba.trafficmanager.net/emea-client-ss.msg/","channelId":"msteams","from":{"id":"29:1aq6GCrC6lM9dv3YkAYi1gxTPiLnojGFgVr0_Th-2x6DhqmHAOhFwQHFzSyDy5RruXY4_FZjJebKHU7bpxfHpXA","name":"ROBERTO ALONSO FERNANDEZ","aadObjectId":"1e0dc7a0-9d5e-488b-bcf2-7e39c84076b8"},"conversation":{"isGroup":true,"id":"19:9e1c52275dfb4d0b873ddf34eb9f4979#thread.skype;messageid=1512376018086","name":null},"recipient":null,"textFormat":"plain","attachmentLayout":null,"membersAdded":[],"membersRemoved":[],"topicName":null,"historyDisclosed":null,"locale":null,"text":"<at>PandoBot</at> fff","speak":null,"inputHint":null,"summary":null,"suggestedActions":null,"attachments":[{"contentType":"text/html","contentUrl":null,"content":"<div><span itemscope=\"\" itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">PandoBot</span> fff</div>","name":null,"thumbnailUrl":null}],"entities":[{"type":"clientInfo","locale":"es-ES","country":"ES","platform":"iOS"}],"channelData":{"teamsChannelId":"19:9e1c52275dfb4d0b873ddf34eb9f4979#thread.skype","teamsTeamId":"19:1e04f564ce5e4596bf2f266dbcff439e#thread.skype","channel":{"id":"19:9e1c52275dfb4d0b873ddf34eb9f4979#thread.skype"},"team":{"id":"19:1e04f564ce5e4596bf2f266dbcff439e#thread.skype"},"tenant":{"id":"9744600e-3e04-492e-baa1-25ec245c6f10"}},"action":null,"replyToId":null,"value":null,"name":null,"relatesTo":null,"code":null}';
//generate HMAC hash with diferent $data formats
$test = [$data, unpak_str($data), base64_encode($data), unpak_str(base64_encode($data))];
hasher($test, $output);
//microsoft provided HMAC
$output.="<HR>EW2993goL1q7nGhytIb3jKmV6luXLz15Bq2aYwuCeiE=";
echo $output;
/*
Calculates:
0HsKoHza/QBvdz+nZw9tOti/eSWjyMMt/U77bfDqiE8=
3jSq3I0HNQkjB9QfnnsxC1c3pF5PjqweHlSVcicrShY=
bTQcGVTHX8/Gh4xovnN0WiJUiNaOQwvUZnwyFfiCaJE=
qHBT2Y2ITyoxz2gmBbG8P1CrClvETus6dTffET3bAR8=
8BcrXEQDDi77qgxCZLYyb/6ez8p9Qg2ZhTyZPWkdn/g=
+8RSU5SSJKxqRLKkI+NkTE01xwu6PwPkKKMuvyyUvlo=
PdL5ZpEwcN6Fe5kfX7zeAZLJvt0uLNTzu7lhuoOcr2o=
s6M5pYruEgWeNMEOFfQRjVKQqtPBVaW3TJb2MzObF2c=
xOTLhddbAwczQVneuTDQhPzmoIXGQljpf27c+hlhQII=
aUMm5b2sKfmwGZOglfiu228fWqoLlwjc7z1QRdIbakE=
5a7bAj9tzqhP9l85OvfVasURW0GSV5rykRutFFPO2fk=
kwg6P2LoDL9rc3SSwJxQeoYJzZYlh+FHFefe38UokBM=
eHeAzI7TV6vYDzxTxwyKWxMeVKFiFlIffWRiIMAk6fk=
ZCyj2UppacQOTXogLPMFLDeMArQg03rhhlIwhynDvng=
uQYK+7u9fppb62zXqtVYfkNK9wVawB3g+BlTyu4dc74=
vjOFA3fqpwUx/VO9dQv3XviNhpjTNQsUwaJIwH4JjdY=
------------ MS PROVIDED HMAC ---------------
EW2993goL1q7nGhytIb3jKmV6luXLz15Bq2aYwuCeiE=
*/
I've zero hash matching...
Finally after lots of trial, it maked me crazy and decided to start a new bot with a new secret. Now works fine. I'm human while MS Teams no... I suppos that was my fault with copy/paste but is a really stranger thing and the other hand old bot fails a lot of times with no response and the newest no
Full example validation HMAC in PHP for Microsoft Teams Custom Bot:
<?php
//The secret share with Microsoft Teams
$secret="jond3021g9imMkrt8txF5AVPIwPFouNV/I72cQFii18=";
//get headers
$a = getallheaders();
$provided_hmac=substr($a['Authorization'],5);
//Get data from request
$data=file_get_contents('php://input');
//json decode into array
$json=json_decode($data, true);
//hashing
$hash = hash_hmac("sha256",$data,base64_decode($secret), true);
$calculated_hmac = base64_encode($hash);
//start log var
$log = "\n========".date("Y-m-d H:i:s")."========\n".$provided_hmac."\n".$calculated_hmac."\n";
try{
//compare hashs
if(!hash_equals($provided_hmac,$calculated_hmac))
throw new Exception("No hash matching");
//response text
$txt="Hi {$json["from"]["name"]} welcome to your custom bot";
echo '{
"type": "message",
"text": "'.$txt.'"
}';
$log .= "Sended: {$txt}";
}catch (Exception $e){
$log .= $e->getMessage();
}
//write log
$fp = fopen("log.txt","a");
fwrite($fp, $log . PHP_EOL);
fclose($fp);
I'm not a PHP expert, and your logic to cover all the cases is a bit convoluted, but I'm pretty sure your problem is that you aren't converting the message ($data) from UTF8 before computing the HMAC.
Here's a simple custom echo bot in Node that shows how to compute and validate the HMAC:
const util = require('util');
const crypto = require('crypto');
const sharedSecret = "+ZaRRMC8+mpnfGaGsBOmkIFt98bttL5YQRq3p2tXgcE=";
const bufSecret = Buffer(sharedSecret, "base64");
var http = require('http');
var PORT = process.env.port || process.env.PORT || 8080;
http.createServer(function(request, response) {
var payload = '';
request.on('data', function (data) {
// console.log("Chunk size: %s bytes", data.length)
payload += data;
});
request.on('end', function() {
try {
// Retrieve authorization HMAC information
var auth = this.headers['authorization'];
// Calculate HMAC on the message we've received using the shared secret
var msgBuf = Buffer.from(payload, 'utf8');
var msgHash = "HMAC " + crypto.createHmac('sha256', bufSecret).update(msgBuf).digest("base64");
console.log("Computed HMAC: " + msgHash);
console.log("Received HMAC: " + auth);
response.writeHead(200);
if (msgHash === auth) {
var receivedMsg = JSON.parse(payload);
var responseMsg = '{ "type": "message", "text": "You typed: ' + receivedMsg.text + '" }';
} else {
var responseMsg = '{ "type": "message", "text": "Error: message sender cannot be authenticated." }';
}
response.write(responseMsg);
response.end();
}
catch (err) {
response.writeHead(400);
return response.end("Error: " + err + "\n" + err.stack);
}
});
}).listen(PORT);
console.log('Listening on port %s', PORT);
You don't need unpack(), or that unpak_str() function (which is also broken because it just overwrites each byte with the next one, not appending them).
Byte arrays are not a thing in PHP - the language doesn't have different string types; how strings are interpreted is entirely up to the functions using them. That is, your shared secret should be just the result of base64_encode($secret).
Is it possible to have PHP scripts send data through to a Node.js server through websockets?
I'm planning a side project that would have PHP scripts running in the background working some magic and the front end application that end users would use would be in Node.js. There would be some socket.io interaction just in Node.js but I'd like the ability to push data into socket.io from the PHP scripts.
I am also working on this.
My implementation is a little different than others.
Most people use php & curl + nodejs & express & socketio
I've done it the following way:
memcache in both php and nodejs (to share the userid and cookie) (you can also use redis)
a custom PHP class to send a request via websocket to localhost, where the nodejs server broadcasts to a user room (all sessions from the same user).
Here is the class I used to communicate from php to socketio (sends only data to nodejs and not the way around!)
When I connect to socket.io, my script reads my php cookie and sends it to the node server, where it accesses the memcache json sessions and identifies the user, joining him to a room.
Here is a php json-serialized memcached session handler class. It is similar to the one I used.
To make a request in php --> socket.io i do the following:
$s = new SocketIO('127.0.0.1', 8088);
$adata = "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain.";
$msg = json_encode(array('event'=> 'passdata','data'=> $adata, 'to'=> 1));
$tr = 0;
$fl = 0;
for ($i = 0 ; $i < 1000; $i++) {
$s->send( 'broadcast', $msg ) ? $tr++ : $fl++;
}
echo "HIT : " . $tr . PHP_EOL;
echo "MISS: " . $fl;
When a (socket.io) request from localhost goes to the server, i run this code:
var is_local = (this_ip === '127.0.0.1' ? true : false);
socket.on('broadcast', function(data) {
if (data.length === 0 ) return;
if (is_local && typeof data === 'string') {
try {
var j = JSON.parse(data);
} catch (e) {
console.log("invalid json # broadcast".red);
return false;
}
if (!j.hasOwnProperty('to') && !j.hasOwnProperty('event')) return false;
io.to(j.to).emit(j.event, j.data);
console.log('brc'.blue + ' to: ' + j.to + ' evt: ' + j.event);
/** #todo remove disconnect & try to create permanent connection */
socket.disconnect();
} else { console.log('brc ' + 'error'.red ); }
});
If i want to pass data from node to php, I simply exec php code on my nodejs server.
like this:
socket.on('php', function(func, data, callback) {
/* some functions */
if (check_usr(session) === false) return;
console.log('php'.green + ' act:' + func);
var cmd = 'php -r \'$_COOKIE["MONSTER"]="' + session + '"; require(\"' + __dirname + '/' + php_[func].exec + '\");\'';
console.log(cmd);
cp.exec(cmd ,
function(err, stdout, stderr) {
if (err == null) {
console.log(typeof callback);
console.log(JSON.parse(callback));
if (callback != null) callback(stdout);
console.log(stdout);
//socket.emit('php', {uid: uid, o: stdout});
console.log('emitted');
} else {
console.log('err '.red + stdout + ' ' + stderr);
}
});
});
The answer is yes, but exact implementation depends on your environment / requirements.
Here is one example I've hacked from a recent project: it sends a message, then waits for a response to end in chr(10) ("\n"). That response must be received within 0.5 seconds otherwise it assumes failure (see the timing loop). You can fiddle with those bits as needed.
Note: $ip and $port need to be passed in.
$retval = false; // final return value will conatin something if it all works
$socket = #socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false || !is_resource($socket)) {
$socket = false;
$this->lastErrorNum = socket_last_error();
$this->lastErrorMsg = 'Unable to create socket: ' . socket_strerror(socket_last_error());
} elseif (!#socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
$this->lastErrorNum = socket_last_error($socket);
$this->lastErrorMsg = 'Unable to set options on socket: ' . socket_strerror($this->lastErrorNum);
#socket_clear_error ( $socket );
} elseif (!#socket_connect($socket, $ip, $port)) {
$this->lastErrorNum = socket_last_error($socket);
$this->lastErrorMsg = 'Unable to connect socket: ' . socket_strerror($this->lastErrorNum);
#socket_clear_error ( $socket );
} else {
// Socket connected - send message
if (!#socket_write($socket, $message, strlen($message))) {
$this->lastErrorNum = socket_last_error($socket);
$this->lastErrorMsg = 'Unable to write to socket: ' . socket_strerror($this->lastErrorNum);
#socket_clear_error ( $socket );
} else {
// Read a response
$receiveStartTime = microtime(true);
$response = '';
socket_set_nonblock ($socket);
while(microtime(true) - $receiveStartTime < 0.5) {
$n = #socket_recv($socket, $dataIn, 1024, 0); // Assume max return value is 1024 bytes.
if ($n) {
$response .= $dataIn;
}
if (strpos($dataIn, "\n") !== false) {
#socket_clear_error ( $socket );
$response = str_replace("\n", '', $response);
break;
}
}
if (socket_last_error($socket) > 0) {
$this->lastErrorNum = socket_last_error($socket);
$this->lastErrorMsg = 'Unable to read from socket: ' . socket_strerror($this->lastErrorNum);
#socket_clear_error ( $socket );
} else {
$retval = $response;
}
}
#socket_close($socket);
}
I need call and pass parameter to NodeJS socket server.
I confirmed the server works well. and now I need to create PHP script to call the function.
My NodeJS code is like this,
var user = {};
socket.sockets.on("connection", function(client){
client.on("messageToClient", function(uid, msg){ // I need to call this method
user[uid].send(msg);
});
});
PHP ::
$fp = fsockopen("10.0.111.10","3000",$errno,$errstr, 30);
if(!$fp){
echo "{$errno} ({$errstr}) <br />";
}else{
$uid_parameter = 'ABC';
$msg_parameter = 'HELLO';
$out = "HOW TO CALL NodeJS messageToClient Method AND PASS PARAMETER?";
fwrite($fp,$out);
fclose($fp);
}
The more elegant solution would be just do it all in Nodejs or all in PHP. DB interaction as well. NodeJS can interact with almost every type of commonly used database. It would save you having to implement what will be a quite hacky code.
I know this question is old, but hasn't content on the internet, so I will leave the answer here for future users.
PHP:
function sio_message($message, $data) {
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, '127.0.0.1', 9501);
if(!$result) {
die('cannot connect '.socket_strerror(socket_last_error()).PHP_EOL);
}
$bytes = socket_write($socket, json_encode(Array("msg" => $message, "data" =>
$data)));
socket_close($socket);
}
NODE.JS:
var express = require('express');
var app = require('express')();
var server = require('http').Server(app);
server.on("connection", function(s) {
//If connection is from our server (localhost)
if(s.remoteAddress == "::ffff:127.0.0.1") {
s.on('data', function(buf) {
console.log('data arrive');
var js = JSON.parse(buf);
console.log(js.data); //Done
});
}
});
Now you can send data from node with only sio_message('anything', 'more data');
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>