Skip to content

Instantly share code, notes, and snippets.

@ZiTAL
Created October 29, 2014 14:53
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ZiTAL/40bb7bafad693dc99c5c to your computer and use it in GitHub Desktop.
Save ZiTAL/40bb7bafad693dc99c5c to your computer and use it in GitHub Desktop.
php: simple websocket client
<?php
/*
based on: http://stackoverflow.com/questions/7160899/websocket-client-in-php/16608429#16608429
FIRST EXEC PYTHON SCRIPT TO GET HEADERS
*/
$ws = new ws(array
(
'host' => '127.0.0.1',
'port' => 8080,
'path' => ''
));
$result = $ws->send('message');
$ws->close();
echo $result;
class ws
{
private $params;
private $head;
private $instance;
public function __construct($params)
{
foreach($params as $key => $value)
$this->params[$key] = $value;
$local = "http://".$this->params['host'];
if(isset($_SERVER['REMOTE_ADDR']))
$local = "http://".$_SERVER['REMOTE_ADDR'];
$this->head = "GET / HTTP/1.1\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Host: ".$this->params['host']."\r\n" .
"Origin: ".$local."\r\n" .
"Sec-WebSocket-Key: TyPfhFqWTjuw8eDAxdY8xg==\r\n" .
"Sec-WebSocket-Version: 13\r\n";
}
public function send($method)
{
$this->head .= "Content-Length: ".strlen($method)."\r\n\r\n";
$this->connect();
fwrite($this->instance, $this->hybi10Encode($method));
$wsdata = fread($this->instance, 2000);
return $this->hybi10Decode($wsdata);
}
public function close()
{
if($this->instance)
{
fclose($this->instance);
$this->instance = NULL;
}
}
private function connect()
{
$sock = fsockopen($this->params['host'], $this->params['port'], $errno, $errstr, 2);
fwrite($sock, $this->head);
$headers = fread($sock, 2000);
$this->instance = $sock;
}
private function hybi10Decode($data)
{
$bytes = $data;
$dataLength = '';
$mask = '';
$coded_data = '';
$decodedData = '';
$secondByte = sprintf('%08b', ord($bytes[1]));
$masked = ($secondByte[0]=='1') ? true : false;
$dataLength = ($masked===true) ? ord($bytes[1]) & 127 : ord($bytes[1]);
if ($masked===true)
{
if ($dataLength===126)
{
$mask = substr($bytes, 4, 4);
$coded_data = substr($bytes, 8);
}
elseif ($dataLength===127)
{
$mask = substr($bytes, 10, 4);
$coded_data = substr($bytes, 14);
}
else
{
$mask = substr($bytes, 2, 4);
$coded_data = substr($bytes, 6);
}
for ($i = 0; $i<strlen($coded_data); $i++)
$decodedData .= $coded_data[$i] ^ $mask[$i % 4];
}
else
{
if ($dataLength===126)
$decodedData = substr($bytes, 4);
elseif ($dataLength===127)
$decodedData = substr($bytes, 10);
else
$decodedData = substr($bytes, 2);
}
return $decodedData;
}
private function hybi10Encode($payload, $type = 'text', $masked = true)
{
$frameHead = array();
$frame = '';
$payloadLength = strlen($payload);
switch ($type)
{
case 'text' :
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;
case 'close' :
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;
case 'ping' :
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;
case 'pong' :
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}
// set mask and payload length (using 1, 3 or 9 bytes)
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]);
// most significant bit MUST be 0 (close connection if frame too big)
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;
// convert frame-head to string:
foreach (array_keys($frameHead) as $i)
$frameHead[$i] = chr($frameHead[$i]);
if ($masked===true)
{
// generate a random mask:
$mask = array();
for ($i = 0; $i<4; $i++)
$mask[$i] = chr(rand(0, 255));
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
// append payload to frame:
for ($i = 0; $i<$payloadLength; $i++)
$frame .= ($masked===true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
return $frame;
}
}
# FIRST exec this script to get the headers to put then in the php script ;)
#
# requirements:
# pip install websocket-client
import websocket
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.create_connection("ws://127.0.0.1:8080")
ws.send("message")
result = ws.recv()
print result
ws.close()
/*
websocket server
requirements:
npm install ws
*/
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer(
{
host: '127.0.0.1',
port: 8080
});
wss.on('connection', function(ws)
{
console.log('connect');
ws.on('message', function(message)
{
console.log('received: %s', message);
});
ws.send('something');
});
@pinich
Copy link

pinich commented Jun 15, 2016

This works well for short messages, but it fails at messages with length of 200,000 chars.
Does anyone has any idea what should I change to allow sending long messages ???

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment