I'm searching for help with my websocket problem. I have built a simple HTML5 websocket to connect between my AngularJS-Site (websocket connects through simple JS) and my PHP-Server. The connection works as well, and also sending and recieving data works as well. The reason, why I need a websocket is: I have a diferent REST-Service (PHP) at the same server, which also communicates with the AngularJS-Site. So the REST-Service changes data at a Database. Now, when I kick an action from the AngularJS-Site (create a new user for example), the REST-Service creates a job in a joblist, and the job will be performed from any other service (not relevant), and after some seconds, the service will give a sign to the REST-Service, if the job is done. The job will be set to done (in the database).
Now, at this point as the job is set to done, i need to send a request from the REST-Service to the PHP Websocket, ant the Websocket should send a message to the angularJS-Site. I know, i could polling through Angular-JS, but that would create too big traffic, beacuse many users will use that system at the same time.
Sorry for my bad explanation (and also for my bad english - i'm german ;)).
My simple question: Is there any possibility to send a request from the PHP REST-Service to the websocket, so the websocket will notify my angular: The job is done.
Is that simple request possible, or have I to create a PHP Client which refreshes over and over the database to check, if the job is done and send then to the angular through the websocket? Any other ideas?
Thanks for your help!
Edit: Maybe as said some code would be good :). There's only some standart-HTML5 websocket, but maybe it would help:
JS (in angulars config-method):
//configure websocket
var uri= "ws://x.x.x:9000/websocket/websocket.php";
ws= new WebSocket(uri);
ws.onopen = function(ev) { // connection is open
console.log("Websocket: Connection established");
}
ws.onmessage = function(ev) {
console.log(ev.data);
};
ws.onerror = function(ev){
console.log("Websocket: Connection Error: " + ev.Error);};
ws.onclose = function(ev){
console.log("Websocket: Connection closed");};
Thats the PHP-Websocket (found on internet):
$host = 'sample.web.net'; //host
$port = '9000'; //port
$null = NULL; //null var
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, 0, $port);
socket_listen($socket);
$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($sock);
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: wss://$host:$port/websocket/websocket.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_conn,$upgrade,strlen($upgrade));
}
I don't think, the REST-Service is relevant.
Edit 2 I think, the only solution for me (after you said, a simple request is not possible) is to create a seperate class (php) which communicates with the websocket. It will open a connection to send, that the job is done, and close it after all. That should work for me, not that graceful, but should work. Thanks for help!
Sam,
Take a look at Thruway, WampPost and Angular-WAMP. These projects use a protocol called WAMP, which allows different components to talk to each other over Websockets.
Your setup will look something like this:
[WampPost Client]<---->[Thruway WAMP Router]<---->[Angular Client]
I'll give you some quick code samples, so you can see how it all works together, but you'll need to go to each individual project to see how to configure each component.
Thruway Router:
<?php
require 'vendor/autoload.php';
use Thruway\Peer\Router;
use Thruway\Transport\RatchetTransportProvider;
$router = new Router();
//Websockets listen on port 9090
$transportProvider = new RatchetTransportProvider("127.0.0.1", 9090);
$router->addTransportProvider($transportProvider);
//WampPost Client listens on port 8181
$router->addInternalClient(new \WampPost\WampPost('realm1', null, '127.0.0.1', 8181));
$router->start();
Angular:
app.config(function ($wampProvider) {
$wampProvider.init({url: 'ws://127.0.0.1:9090/',realm: 'realm1'});
})
app.run(function($wamp){
$wamp.open(); //This will open the connection when the app starts
})
app.controller("MyCtrl", function($scope, $wamp) {
// Subscribe to a topic
function onevent(args) {
$scope.hello = args[0];
}
$wamp.subscribe('com.myapp.hello', onevent);
});
Publishing a message with Request/Response. This can be done from PHP, Curl or anything that can make an HTTP request.
curl -H "Content-Type: application/json" -d '{"topic": "com.myapp.hello", "args": ["Hello, world"]}' http://127.0.0.1:8181/pub
Now everyone that has subscribed to the topic "com.myapp.hello", will receive the message "Hello, world".
This is just a very basic example. You can do a lot more with WAMP, including RPCs and websocket authentication.
Also, I'm one of the developers of Thruway, so if you have any issues or general questions, let me know.