How to use Ratchet to respond to HTML5 server-side events? - php

(Note: I've intentionally put non adequate websocket tag here, as it's best chance for WebSocket expert folks to know architecture of Ratchet).
I'm up for implementing HTML5 server side events, and what I need is server side solution. Since hanging Apache's one process per connection (connection pool limit, memory consumption...) is out of consideration I was hoping that Ratchet project can be of help, since it's most maintained project and they have http server coupled along with other components.
My question is: how can I use it? Not for upgrading http request (default usage), but for serving dynamically generated content.
What have I tried so far?
installed Ratchet as explained in tutorial
tested WebSocket functionality - works properly
followed very basic set of instructions given on page that describes http server component:
/bin/http-server.php
use Ratchet\Http\HttpServer;
use Ratchet\Server\IoServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$http = new HttpServer(new MyWebPage);
$server = IoServer::factory($http);
$server->run();
One should not be an expert to figure out that MyWebPage class here needs to be declared in order for server to work, but how?
The Ratchet documentation does not seems to cover this.

Your MyWebPage class needs to implement HttpServerInterface. Since it's just going to be a simple request/response you need to send a response and then close the connection within the onOpen() method of your class:
<?php
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Ratchet\ConnectionInterface;
use Ratchet\Http\HttpServerInterface;
class MyWebPage implements HttpServerInterface
{
protected $response;
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null)
{
$this->response = new Response(200, [
'Content-Type' => 'text/html; charset=utf-8',
]);
$this->response->setBody('Hello World!');
$this->close($conn);
}
public function onClose(ConnectionInterface $conn)
{
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
}
public function onMessage(ConnectionInterface $from, $msg)
{
}
protected function close(ConnectionInterface $conn)
{
$conn->send($this->response);
$conn->close();
}
}
I ended up using the Ratchet\App class instead of Ratchet\Http\HttpServer because it allows you to set up routing among other things, so your /bin/http-server.php would then look like this:
<?php
use Ratchet\App;
require dirname(__DIR__) . '/vendor/autoload.php';
$app = new App('localhost', 8080, '127.0.0.1');
$app->route('/', new MyWebPage(), ['*']);
$app->run();
When you run php bin/http-server.php and visit http://localhost:8080 you should see the Hello World! response in your browser.
This is all you need for a basic request/response system, but it could be extended further by implementing HTML templates and things like that. I've implemented this myself in a little test project which I've uploaded to github along with a lot of other things, including an abstract controller which I can extend for different pages.

Chat server using Ratchet - Basic
Chat server using Ratchet - Advanced
Check the link above. The guy here is using Ratchet to build a real time chat server. He is basically storing usernames initially and then sending/broadcasting to all. You can modify it and check at the time of sending that certain username or uid is active at the moment and send data to them only. You can generate data dynamically and send to particular users or to all. May be this will help.

Related

How do I make a guzzle response into a response to a request from a client?

I have an application running on webserver A. I have a second application running on webserver B. Both webservers require a login. What I need to do is have a request to webserver A pass through to webserver B and return a file to the client without having the client login to Webserver B. (In other words, webserver B will be invisible to the client and I will take care of the auth credentials with my request to B from A). The code below is built on a laravel framework, but I don't believe the answer needs to be laravel specific.
The code works but it is only returning the HEAD information of the file to the calling client. Not the file itself.
Any help will be greatly appreciated!
Controller:
public function getAudioFile(Request $request)
{
//This is the id we are looking to pull
$uid = $request->uniqueid;
$audioServices = new AudioServices();
return $audioServices->getWavFile($uid);
}
Service:
public function getWavFile(String $uniqueId)
{
$client = new GuzzleHttp\Client(['verify' => false]);
return $client->request('GET', $this->connectString.$uniqueId, ['auth' => ['username', 'password']]);
}
As mentioned by bishop you can use sink option from Guzzle to stream the response of a Guzzle request.
You can pass that stream to a response from your controller. I'm not sure if Laravel has built-in stream support, but the underlying symfony httpfoundation components do. An example of it's usage can be found in this tutorial.
If you prefer not to use the sink option from Guzzle you can also use the response itself as that implements PSR-7 stream objects.

Ratchet Websocket push integration

I am trying to develop a chat system using Websockets (Ratchet). Until now I have already made a functional PHP based Websocket server that is able to answer on predefined JSON encoded message using the following method.
function onMessage(ConnectionInterface $from, $msg){ ... }
The problem is that I'd like to push messages from the backend database via a background worker/thread to the right client, if something changed in my tables. Is this possible using PHP?
I don't want the client asking the websocket server every 5 minutes to refresh its state as shown below.
{"action":"giveMeUpdates"}
but the webserver should be able to do something like this:
{"status":"newMessages", "messagelist":[...]}
In addition:
class Chat extends ChatActionHandler implements MessageComponentInterface { ... }
this is my class where the ChatActionHandler holds functions for interacting with client requests. With the MessageComponentInterface i can only reply on Socket events like function onOpen(ConnectionInterface $conn). It is runned by the RatchetIO Server:
$server = IoServer::factory(
new Chat(),
8080);
$server->run();
You can attach timer, like cron with
$this->loop->addPeriodicTimer($timeout, function($timer) {});

Using reactive PHP in a blocking application

I'm currently working on a PHP application that will be using some websocket connections to talk to another service.
To talk to this websocket service, we are using Ratchet - which is a PHP library based on react PHP.
This piece of code needs to send and respond to a couple of requests, and after that, should return the information to the "main thread".
Example flow:
HTTP request -> controller -> Starts a service which opens a websocket client -> websocket client is talking to server -> once its done it should return the outcome to the controller code -> controller outputs to user
The issue I'm having is that I'm not familiar with Reactive PHP and am not sure how to handle this.
I've tried;
$service = new WebsocketService();
$startTimer = time();
$service->getList(44);
while($service->getResponse() == null) {
usleep(500);
if (time() > $startTimer + 10) {
continue; //Timeout on 10 seconds
}
}
var_dump($service->getResponse());
The service code would set its "response" variable to something other than null once its done. This obviously fails, because the sleep method is blocking the thread. Also without, it seems like the while loop is blocking I/O and the reactive code fails.
A solution would be to open up a new thread and run the websocket code there, but I wouldn't be happy with that.
I feel like I need to implement some sort of "watcher" around the websocket process, but I'm not sure how to do that.
Our Websocket service client code looks like this;
private $response = null;
/**
* #return null|object
*/
public function getResponse() {
return $this->response;
}
public function getList($accountId) {
$this->response = null;
\Ratchet\Client\connect('ws://192.168.56.1:8080')->then(function(\Ratchet\Client\WebSocket $conn) use ($accountId) {
$login = new \stdClass();
$login->action = 'login';
$conn->on('message', function($msg) use ($conn, $login, $accountId) {
try {
$response = json_decode($msg);
if ($response->result_id == 100) {
//Succesfully logged in to websocket server
//Do our request now.
$message = new \stdClass();
$message->target = 'test';
$conn->send(json_encode($message));
}
if (isset($response->reply) && $response->reply == 'list') {
$this->response = $response; //This is the content I need returned in the controller
$conn->close(); //Dont need it anymore
}
} catch (\Exception $e) {
echo 'response exception!';
//Do nothing for now
}
});
$conn->send(json_encode($login));
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
}
Running the code like this also does not work;
$service = new WebsocketService();
$service->getList(44);
echo 'Test';
var_dump($service->getResponse());
because the "test" echo comes before I even get a response from the websocket server.
Please, enlighten me! I'm not sure what to search for.
PHP and websockets still seem to be a bit experimental. Nevertheless I have found a great tutorial on medium.com, written by Adam Winnipass which should be really helpful for solving your problem: https://medium.com/#winni4eva/php-websockets-with-ratchet-5e76bacd7548
The only difference is that they are implementing their websocket client with JavaScript instead of PHP. But in the end there should not be much of a difference, because as soon as we have opened the Websocket connection of each end both applications have to send and also wait to receive notifications - this is how they illustrate it:
Seems like one possibility to create a successful Websocket connection is to extend the MessageComponentInterface
use Ratchet\MessageComponentInterface;
which also requires
use Ratchet\ConnectionInterface;
The message component interface defines the following methods:
onOpen
onMessage
onClose
onError
And I think this is how the Ratchet library is implementing it. This is how they are finally starting their server:
use Ratchet\Server\IoServer;
use MyApp\MyCustomMessageComponentInterface;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new MyCustomMessageComponentInterface()
)
),
8080
);
$server->run();
With this architecture you already can receive (onMessage) and sending is also possible with the send() method.
I can not solve the exact problem with your existing code. But I guess if you are using the pre-built classes and interfaces of the library as intended (and demonstrated here) you should be able to achieve what you want by adding your code to the corresponding methods.
More information and examples can be found in the docs:
http://socketo.me/docs/server
http://socketo.me/api/namespace-Ratchet.html
Are you extending class with WsServer, This might be issue, if you are getting fatal errors. I am not sure whether you are getting fatal errors or warnings. Also i notice the public function onOpen() opens a connection. Please try referring this document http://socketo.me/api/class-Ratchet.WebSocket.WsServer.html might be useful.

Creating/Managing private rooms using Ratchet?

I am making iOS chat application. After doing study on needed technology and protocols, I decided to give a websockets try. For reasons our stack top is php based and I came to know about ratchet as websockets for PHP. I made simple chat server for ios front-end from reading documentation. Chat is working very fine and I am comfortable with it too. I wanted to know on how to create separate private chat rooms. Will different instance of socket event loop needed to run for separate rooms ?
The sample server I made is using single event loop for managing user connections and dispatching messages to different connection/user id's. I really tried to look out for creating private chat rooms but haven't found any information where I could be confident.
Will I have to manage each connection/user id's virtually on this event loop, like deciding which users can chat to each other directly by controlling the dispatching of messages ? Or is their really a separate way to do this ? This is the event loop sample as per documentation that I implemented:
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
$server->run();
I want to tell that I am a iOS/Android developer and really no expert or have fluent knowledge on web frontend/backend technologies.
Will different instance of socket event loop needed to run for separate rooms ?
No. Only one loop is needed. Your snippet is fine. What you have to do is to adjust Chat class so that it accepts additional parameter from the user input - room id/name.
For example, a user sends the message {"cmd":"msg", "message":"Hi", "room": 1}, then Chat should send the message only to the users who joined that room. And, of course, you should implement other user methods, such as {"cmd":"join", "room": 1}, {"cmd":"leave", "room": 1}
Well i might be a little bit late to answer, but here how i did that.
You should implement WampServerInterface instead of MessageComponentInterface on your Chat class (If you are not already doing so).
As said above your snippet is fine.
Here my Chat class :
class Chat implements WampServerInterface
{
protected $conversationId;
public function __construct(){
$this->conversationId = null;
}
public function onSubscribe(ConnectionInterface $conn, $conversation_id){
$this->conversationId = $conversation_id;
echo "Client $conn->resourceId assigned to the conversation : $conversation_id\n";
}
public function onPublish(ConnectionInterface $conn, $conversation_id, $event, array $exclude, array $eligible){
echo "Message sent to $conversation_id : $event";
// ... save in Database or else
// Send data to conversation
$this->conversationId->broadcast($message);
}
}
This is for a connection to one room only.
However if you want to have multiple chatrooms running in same time, you should have a look to Ratchet code on Github.
I don't know what did you use for the frontend, i personally use autobahn.js to instantiate the connection with the server (using ratchet).

Problems implementing a PHP Thrift server

I'm currently trying to create a PHP Thrift server which will be accessed by a PHP client to perform addition of two integers. Just a basic implementation to get the basics of a Thrift client/server working, but I'm not totally clear on how to set up the PHP server side of things. This is being done on Amazon EC2 localhost. Excuse the names of things - I ran out of variations of the word test.
This is the basic Thrift IDL - http://pastebin.com/3KGGrDUN
namespace php gaybear
service addTest {
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
}
This is currently the code for the server side of things - http://pastebin.com/CWnernxf
<?
$GLOBALS['THRIFT_ROOT'] = '/usr/lib/php';
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TPhpStream.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
$GEN_DIR = '/usr/lib/php/gen-php/gaybear/';
require_once $GEN_DIR.'/addTest.php';
require_once $GEN_DIR.'/gaybear_types.php';
class addHandler {
public function ping() {
}
public function add($num1, $num2) {
return $num1 + $num2;
}
public function zip() {
}
}
$handler = new addHandler();
$processor = new addTest($handler);
$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));
$protocol = new TBinaryProtocol($transport, true, true);
$transport->open();
$processor->process($protocol, $protocol);
$transport->close();
?>
I'm not sure how you go about setting up the server side of things. For other languages it seems to be as simple as defining a socket, but from reading many tutorials PHP seems to use "TPhpStream".
Is there anyone that could shed some light into creating a Thrift PHP server and getting a PHP client to call basic procedures from it? I haven't found a tutorial that has explained the creation of Thrift PHP server well enough for me to understand.
Thanks.
Not in English, but much code samples and you can try to use Google translate.
http://www.easy-coding.de/wiki/php/thrift-php-server.html

Categories