Creating/Managing private rooms using Ratchet? - php

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).

Related

DDD and UUID with API : generation in client or server side?

at my work, we are currently struggling with uuid generation approach in a web application.
We do DDD and our persistent storage is a remote API that we own.
here is simplified exemple of my code:
class HireFooHandler() {
private $repository;
private $mailer;
public function __construct(FooRepository $repository, HiringMailer $mailer, ... ) {
$this->repository = $repository; //behind the repository, there are api calls
$this->mailer = $mailer;
...
}
public function handle(HireFooCommand $command) {
try {
$foo = new Foo($command->uuid, $command->baz, ...);
$this->repository->hire($foo);
$this->mailer->sendHiredMail($foo);
...
} catch(...) {
...
}
}
}
Some of my co worker are not confortable with the app generating and sending the uuid to the api.
They prefer to let the server side handle the uuid generation and if in this exemple "foo" is saved, send back the uuid generated to the app in the response.
The main argument is that, the api will be public too and they dont want the client having to generate the uuid.
On the other hand, from the point of view of my application and coding, as my Entity Foo should not be in an invalid state, to create it, i have to provided the uuid and so generate it from the client side, which i think is ok.
So here i'm, struggling with this and dont know what is the best approach, and where to generate my uuid ? client side ? server side ?
I proposed them to make the api more flexible by adding a uuid optional field in the post endpoint that can be validated. So the Api can received a uuid from the client and if not generate it's own.
But i'm not sure this is a good practice.
If someone have some inputs, i'll be glad to read it :)
You should understand why it is good to have uuid generation on a client-side, the biggest benefit for me is to allow doing async or complex stuff. For example, the client can send a request to a server and without waiting for a response, started to work with generated uuid, this approach allows the client to get a lot of benefit from uuid, as well as your php app when you started using uuid instead of dd, because you are no longer wanting and can send events for example. Also, there is a kind of middle solution, generating the uuid in the controller while creating command. There is more interesting concept: commands never failed.

Ratchet + Symfony3: how to get access to current server from outside

Here is my Symfony3 command that I'm using for websocket server purpose
public function __construct(ChatFlowProcessor $chatManager, int $webSocketPort)
{
$this->chatManager = $chatManager;
$this->webSocketPort = $webSocketPort;
parent::__construct();
}
$server = IoServer::factory(
new HttpServer(
new WsServer(
$this->chatManager
)
),
$this->webSocketPort
);
$server->run();
As you see, I've got a chatManager simply using Symfony3 autowiring. The service implements Ratchet MessageComponentInterface.
Now, I want to get access to the server from outside of the connection. I mean, send a message to websocket client using my chatManager, apparently I need to get access to chatManager instance, that stored in WsServer and keep information about all active connections.
Is it possible? Thanks.
You dont need an access to chatManager just to send messages. It's a chat server which only purpose IS to transfer messages from/to all its clients.
Simply create a websocket client, connect it to your server (together other clients) and start sending (and receiving) messages. Any special functionality (e.g. send message to one client only or get list of all clients etc.) must be implemented in chatManager (Ratchets MessageComponentInterface).
Perhaps I misunderstood the question, sorry then.

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) {});

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

(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.

PHP & SOAP what use is a message broker?

I'm working on a solution to make certain data from a large database available to a remote website. My first thought was to simply cook up some soap web services to fetch certain data from the database. This could be done in just a few line, for instance like this, with the user of Zend_Soap_Server:
class MyClass
{
public function getlastname($id)
{
$dbh = new PDO("oci:dbname=bigdb", "theuser", "thepass");
$stmt = $dbh->prepare("select lastname from person where id = :id");
if ($stmt->execute(array(':id',$id)))
{
$row = $stmt->fetch();
return $row['lastname'];
}
}
}
$server = new Zend_Soap_Server(null, $options);
$server->setClass('MyClass');
$server->setObject(new MyClass());
$server->handle();
Now someone told me to also have a look at message brokers / queues. I've been taking a look at some software like apache activeMQ, stomp and zend_queue but I didn't really get a clear view what they should be used for and weather they would be useful in this project.
I do understand that my implementation could have some drawbacks, like a sluggish website when the database is not responding quickly and a high load on the database when there are coming lots of requests from the website, would a message broker be able to prevent such complications?
The role of a message broker is to check the requests and dispatch them to the right service or return a response from a cache.
If you are expecting large traffic you probably should consider using a message broker.
Regards,
Alin

Categories