I am trying to develop a websocket server in php.
Here is what I have done so far :
(server part)
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, "localhost", 10000);
socket_listen($server);
$client = socket_accept($server);
$message = socket_read($client, 5000);
$matches = array();
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $message, $matches);
$new_key = new_key($matches[1]);
$new_message = "HTTP/1.1 101 Switching Protocols\r\n";
$new_message .= "Upgrade: websocket\r\n";
$new_message .= "Connection: Upgrade\r\n";
$new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
socket_write($client, $new_message, strlen($new_message));
$new_message = "Test message !";
socket_write($client, $new_message, strlen($new_message));
function new_key($key)
{
$key .= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
$key = sha1($key);
$new_key = '';
for ($i = 0; $i < strlen($key); $i+=2)
{
$new_key .= chr(intval($key[$i] . $key[$i+1], 16));
}
$new_key = base64_encode($new_key);
return $new_key;
}
/* End of file server.php */
(client part)
window.onload = function() {
var ws = new WebSocket('ws://localhost:10000');
ws.onopen = function() {
alert('Connection open !');
setInterval(function() {
ws.send('lol');
}, 1000);
}
ws.onclose = function() {
alert('WebSocket close !');
}
ws.onmessage = function(e) {
alert(e.data);
}
ws.onerror = function(e) {
alert('Error');
}
};
The first socket_write works perfectly (for the handshake) and the open event is triggered correctly. But the second socket_write doesn't work. I tried to do a ws.send on the open event with a second socket_read in server part, it doesn't work neither.
Thank you if you have an idea !
I think it is a INVALID_STATE_ERR: DOM Exception 11 meaning a sync problem. Try this in your client:
var test = function(){
var myWebSocket = new WebSocket('ws://localhost:10000');
myWebSocket.onopen = function(e){
alert('connection open');
this.send('test1');
this.send('test2');
}
myWebSocket.onmessage = function(e){ alert('message ' + e.data) };
myWebSocket.onclose = function(e){ alert('connection closed'); this.close(); };
myWebSocket.send('test3');
};
test3 will fail because it is an asynchronous websocket.
I had a similar issue. After checking out this project over here https://github.com/srchea/PHP-Push-WebSocket I noticed a function called "encode" in this file https://github.com/srchea/PHP-Push-WebSocket/blob/master/lib/Server.class.php before sending the message.
So try to change this
socket_write($client, $new_message, strlen($new_message));
$new_message = "Test message !";
socket_write($client, $new_message, strlen($new_message));
to this
socket_write($client, $new_message, strlen($new_message));
$new_message = "Test message !";
socket_write($client, chr(129) . chr(strlen($new_message)) ."". $new_message);
Related
I use socket.io for socket app in my client side. For the server I use socket library of PHP.
After the handsake, the connection was closed and generated a warning
failed: WebSocket is closed before the connection is established.
socket_getsockname($socket_server, $addr, $port);
while($socket = socket_accept($socket_server)) {
$handshake = false;
if(!$socket){
print "error \n";
}
socket_getpeername($socket, $raddr, $rport);
$msg = "Welcome aboard !";
$length = strlen($msg);
print socket_read ( $socket , 255 )."\n";
$error = socket_last_error($socket);
$error_message = socket_strerror($error);
if(!$handshake){
$bytes = socket_recv($socket,$buffer,2048,0);
list($resource,$host,$origin,$key,$key1,$key2,$l8b) = getheaders($buffer);
$accept = base64_encode(SHA1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
$upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" .
"Upgrade: WebSocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Origin: " . $origin . "\r\n" .
"Sec-WebSocket-Location: ws://" . $host . $resource . "\r\n" .
"Sec-WebSocket-Accept: " . $accept . "\r\n\r\n";
if(socket_write($socket,$upgrade.chr(0),strlen($upgrade.chr(0)))) {
$handshake = true;
}
}
else{
if(socket_write ( $socket , $msg,$length)){
print "Mesg envoyee \n";
}
}
do{
$read = socket_read($socket, 1080);
print $read."\n";
if(socket_write ( $socket , $msg,$length)){
print "Mesg envoyee \n";
}
} while ($read != '');
in my server side in PHP
in my client :
var socket = io('http://localhost:4454',{
agent: false,
transports: ['websocket']
});
console.log(socket);
socket.on('connect', () => {
socket.send('hi');
console.log(socket);
});
socket.emit('foo');
socket.on('message', (data) => {
console.log(data);
console.log(socket);
});
socket.on('data', (data) => {
console.log(data);
});
socket.on('error', (error) => {
console.log(error);
});
//blocage des reconnection si erreurs
var attemp = 5;
socket.on('reconnecting', (attemp) => {
console.log("I'm sad :(");
//socket.close();
});
I don't find any solution for this issue.
I don't understand why in localhost stream was automatically close.
Server side looked fine, in this time...
i've just recently been trying to get a Web socket working for my new client chat.
I'm running XAMPP on localhost and have made sure my socket locations are correct.
The socket connects to the chat due to chat messaged or Connected! but when I refresh the page, the messages are lost and the command prompt shows this error:
Image of command prompt error
Here is my server.php code :
<?php
$host = 'localhost'; //host
$port = '900'; //port
$null = NULL; //null var
//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//reuseable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
//bind socket to specified host
socket_bind($socket, 0, $port);
//listen to port
socket_listen($socket);
//create & add listning socket to the list
$clients = array($socket);
//start endless loop, so that our script doesn't stop
while (true) {
//manage multipal connections
$changed = $clients;
//returns the socket resources in $changed array
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
$header = socket_read($socket_new, 1024); //read data sent by the socket
perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
socket_getpeername($socket_new, $ip); //get ip address of connected socket
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
send_message($response); //notify all users about new connection
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
//loop through all connected sockets
foreach ($changed as $changed_socket) {
//check for any incomming data
while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$received_text = unmask($buf); //unmask data
$tst_msg = json_decode($received_text); //json decode
$user_name = $tst_msg->name; //sender name
$user_message = $tst_msg->message; //message text
$user_color = $tst_msg->color; //color
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color)));
send_message($response_text); //send data
break 2; //exist this loop
}
$buf = #socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) { // check disconnected client
// remove client for $clients array
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
//notify all users about disconnected connection
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
send_message($response);
}
}
}
// close the listening socket
socket_close($socket);
function send_message($msg)
{
global $clients;
foreach($clients as $changed_socket)
{
#socket_write($changed_socket,$msg,strlen($msg));
}
return true;
}
//Unmask incoming framed message
function unmask($text) {
$length = ord($text[1]) & 127;
if($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
}
elseif($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
}
else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
//Encode message for transfer to client.
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$text;
}
//handshake new client.
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];
}
}
$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));
}
It gets to the lines 52, 53, and 54 when the errors hit.
It can't read the current users details.
My script for my main chat page is :
<script type="text/javascript">
$(document).ready(function() {
//create a new WebSocket object.
var wsUri = "ws://localhost:900/New%20Login%20System/HazzJay/chat/server.php";
websocket = new WebSocket(wsUri);
websocket.onopen = function(ev) { // connection is open
$('#message_box').append("<div class=\"system_msg\">Connected!</div>"); //notify user
}
$('#send-btn').click(function(){ //use clicks message send button
var mymessage = $('#message').val(); //get message text
var myname = $('#name').val(); //get user name
if(myname == ""){ //empty name?
alert("Enter your Name please!");
return;
}
if(mymessage == ""){ //emtpy message?
alert("Enter Some message Please!");
return;
}
document.getElementById("name").style.visibility = "hidden";
var objDiv = document.getElementById("message_box");
objDiv.scrollTop = objDiv.scrollHeight;
//prepare json data
var msg = {
message: mymessage,
name: myname,
color : '<?php echo $colours[$user_colour]; ?>'
};
//convert and send data to server
websocket.send(JSON.stringify(msg));
});
//#### Message received from server?
websocket.onmessage = function(ev) {
var msg = JSON.parse(ev.data); //PHP sends Json data
var type = msg.type; //message type
var umsg = msg.message; //message text
var uname = msg.name; //user name
var ucolor = msg.color; //color
if(type == 'usermsg')
{
$('#message_box').append("<div><span class=\"user_name\" style=\"color:#"+ucolor+"\">"+uname+"</span> : <span class=\"user_message\">"+umsg+"</span></div>");
}
if(type == 'system')
{
$('#message_box').append("<div class=\"system_msg\">"+umsg+"</div>");
}
$('#message').val(''); //reset text
var objDiv = document.getElementById("message_box");
objDiv.scrollTop = objDiv.scrollHeight;
};
websocket.onerror = function(ev){$('#message_box').append("<div class=\"system_error\">Error Occurred - "+ev.data+"</div>");};
websocket.onclose = function(ev){$('#message_box').append("<div class=\"system_msg\">Connection Closed</div>");};
});
</script>
I want it to load the previous messages in the chat even if a page is refreshed.
I have process on server which acts as WebSocket server (not written in Ratchet). I want to be able to send data to this process using PHP (as client).
I found a lot of examples to send as TCP like this:
<?php
$addr = gethostbyname("localhost");
$client = stream_socket_client("tcp://$addr:8887", $errno, $errorMessage);
if ($client === false) {
throw new UnexpectedValueException("Failed to connect: $errorMessage");
}
fwrite($client, "GET / HTTP/1.0\r\nHost: localhost\r\nAccept: */*\r\n\r\n");
echo stream_get_contents($client);
?>
All I need I to send message to the process and close the connection. The result that I expect is the result from the webSocket will be later printed or "echo" to the PHP page.
Is there a way to make it work with curl in php?
I have found this code on github, (I can't find the exact repo where I got it from because I have looked and tried a lot of them)
<?php
class WebsocketClient {
private $_Socket = null;
public function __construct($host, $port) {
$this->_connect($host, $port);
}
public function __destruct() {
$this->_disconnect();
}
public function sendData($data) {
// send actual data:
fwrite($this->_Socket, "\x00" . $data . "\xff") or die('Error:' . $errno . ':' . $errstr);
$wsData = fread($this->_Socket, 2000);
$retData = trim($wsData, "\x00\xff");
return $retData;
}
private function _connect($host, $port) {
$key1 = $this->_generateRandomString(32);
$key2 = $this->_generateRandomString(32);
$key3 = $this->_generateRandomString(8, false, true);
$header = "GET /echo HTTP/1.1\r\n";
$header.= "Upgrade: WebSocket\r\n";
$header.= "Connection: Upgrade\r\n";
$header.= "Host: " . $host . ":" . $port . "\r\n";
$header.= "Origin: http://localhost\r\n";
$header.= "Sec-WebSocket-Key1: " . $key1 . "\r\n";
$header.= "Sec-WebSocket-Key2: " . $key2 . "\r\n";
$header.= "\r\n";
$header.= $key3;
$this->_Socket = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($this->_Socket, $header) or die('Error: ' . $errno . ':' . $errstr);
$response = fread($this->_Socket, 2000);
/**
* #todo: check response here. Currently not implemented cause "2 key handshake" is already deprecated.
* See: http://en.wikipedia.org/wiki/WebSocket#WebSocket_Protocol_Handshake
*/
return true;
}
private function _disconnect() {
fclose($this->_Socket);
}
private function _generateRandomString($length = 10, $addSpaces = true, $addNumbers = true) {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"ยง$%&/()=[]{}';
$useChars = array();
// select some random chars:
for ($i = 0; $i < $length; $i++) {
$useChars[] = $characters[mt_rand(0, strlen($characters) - 1)];
}
// add spaces and numbers:
if ($addSpaces === true) {
array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');
}
if ($addNumbers === true) {
array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9));
}
shuffle($useChars);
$randomString = trim(implode('', $useChars));
$randomString = substr($randomString, 0, $length);
return $randomString;
}
}
$WebSocketClient = new WebsocketClient('localhost', 8887);
echo $WebSocketClient->sendData("MyUserNameFromPHP");
unset($WebSocketClient);
?>
Out of 7 php websocket clients that I tried, this is the only one that I was able to work with.
It doesn't requires any external files or frameworks.
This is simple implementation for executing short command that doesn't require persistent connection to webSocket server.
I hope it helps you guys , and you will save some time !!!
I'm trying to create a bridge between my application created in PHP and Node.JS.
Node.JS creates socket and listening to it, my code:
var net = require('net'),
fs = require('fs');
var path = '/tmp/echo.sock';
fs.unlink(path, function () {
var server = net.createServer(function(c) {
console.log('server connected');
c.on('close', function() {
console.log('server disconnected');
});
c.write('hello\r\n');
c.on('data', function(data) {
console.log('Response: "' + data + '"');
c.write('You said "' + data + '"');
});
});
server.listen(path, function(e) {
console.log('server bound on %s', path);
});
});
process.on('uncaughtException', function (err) {
console.log( "UNCAUGHT EXCEPTION " );
console.log( "[Inside 'uncaughtException' event] " + err.stack || err.message );
});
And my PHP code just connecting with exists socket and send some data:
$fp = fsockopen("unix:///tmp/echo.sock", -1, $errno, $errstr);
if (!$fp) {
return "ERROR: $errno - $errstr<br />\n";
} else {
fwrite($fp, "Hello World <3");
$out = fread($fp, 8192);
fclose($fp);
return $out; // That code is in function.
}
Everything should working, but in Node.JS console I see response:
server bound on /tmp/echo.sock
server connected
Response: "Hello World <3"
UNCAUGHT EXCEPTION
[Inside 'uncaughtException' event] Error: write EPIPE
at exports._errnoException (util.js:745:11)
at Object.afterWrite (net.js:763:14)
server disconnected
And in PHP I see just first message, hello. Why and how can I fix that?
try this;
<?php
$fp = fsockopen("unix:///tmp/echo.sock", -1, $errno, $errstr);
if (!$fp) {
return "ERROR: $errno - $errstr<br />\n";
} else {
$out = fread($fp, 8192);
fwrite($fp, "Hello World <3");
$out2 = fread($fp, 8192);
fclose($fp);
echo $out; // That code is in function.
echo $out2; // That code is in function.
}
?>
I know where's the problem. In PHP I close the connection to the pipe after write data. Node tries to write response but it can't, because connection in PHP is already closed. So, I just added checking is the connection still up. Of course, this below is not used in production, but just to show how it works.
Node.JS Code:
var net = require('net'),
fs = require('fs');
var path = '/tmp/echo.sock',
toSend = 1,
sended = 0;
fs.unlink(path, function () {
var server = net.createServer(function(c) {
console.log('server connected');
c.on('close', function() {
console.log('server disconnected');
});
c.write('hello\r\n');
c.on('data', function(data) {
console.log('Response: "' + data + '"');
if( data == "IS_ALREADY_CLOSED" & toSend == sended ) {
c.write('ALREADY_CLOSED');
return;
} else {
c.write('NOT_ALREADY_CLOSED');
return;
}
c.write('You said "' + data + '"');
sended++;
});
});
server.listen(path, function(e) {
console.log('server bound on %s', path);
});
});
process.on('uncaughtException', function (err) {
console.log( "UNCAUGHT EXCEPTION " );
console.log( "[Inside 'uncaughtException' event] " + err.stack || err.message );
});
And in PHP:
$fp = fsockopen("unix:///tmp/echo.sock", -1, $errno, $errstr);
if (!$fp) {
return "ERROR: $errno - $errstr<br />\n";
} else {
fwrite($fp, "Hello World <3");
do {
$output = fread($fp, 8192);
if($output == "ALREADY_CLOSED") break;
elseif( $output == "NOT_ALREADY_CLOSED" ) continue;
echo $output . "\n";
fwrite($fp, "IS_ALREADY_CLOSED");
} while(true);
fclose($fp);
}
Server side:
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '127.0.0.1', 8000);
socket_listen($socket);
$client = socket_accept($socket);
$handshake = socket_read($client, 1024);
preg_match('/Sec-WebSocket-Key\: (.+?)\r\n/', $handshake, $accept);
$accept = base64_encode(sha1("$accept[1]258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
$handshake = "HTTP/1.1 101 Switching Protocols\r\n";
$handshake .= "Upgrade: websocket\r\n";
$handshake .= "Connection: Upgrade\r\n";
$handshake .= "Sec-WebSocket-Accept: $accept\r\n\r\n";
socket_write($client, $handshake.chr(0), strlen($handshake.chr(0)));
socket_write($client, 's'.chr(0));
sleep(5);
socket_close($client);
socket_close($socket);
?>
Client side:
<div id="output"></div>
<script>
if ("WebSocket" in window) {
var socket = new WebSocket('ws://127.0.0.1:8000');
socket.onopen = function() {
document.getElementById('output').innerHTML += 'socket open<br />';
}
socket.onmessage = function(msg) {
document.getElementById('output').innerHTML += 'new msg: '+msg.data+'<br />';
}
socket.onclose = function() {
document.getElementById('output').innerHTML += 'socket close';
}
}
</script>
I get "socket open" and after 5 seconds "socket close". I don't get the "s" sent by the server.
Tried on Firefox and Chrome.
What am I doing wrong?
Messages between client and server are not plain text. See the data framing section of the protocol spec for details on how to encode/decode messages.
There are plenty of code snippets in other SO questions (e.g. here) that might help get you started.