问题
Before asking this question, I did my best by reading severel questions on SO (tagged Ratchet and dealing with similar issues but to no avail. I even asked a question which received no attention and I therefore deleted it to write another one (that hopefully is more clear).
My final goal is to build a one-to-one private chat application using Ratchet. Everything is working fine except that I can't send message to a specific user.
Every logged in user connects to the websocket server while accessing secured area of website:
$(document).ready(function() {
var conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connection established!");
// Here I need to send the logged in user_id to websocket server
// and get it in onOpen method so that I can index my array
// of connections with user_id instead of
//$connection->ResourceId, I explain more below
};
conn.onmessage = function(e) {
console.log(e.data);
};
});
When a user writes a message in the chat box, the message is sent via AJAX to web server then pushed to Websocket using ZeroMQ. In the controller:
// Persistence of Message(message_id, sender_id, receiver_id, message_text)
.....
$context = new \ZMQContext();
$socket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$pushData = array(
'receiver_id' => $receiver_id,
'sender_id' => $user->getId(),
'message' => $message->getMessageText(),
);
$socket->send(json_encode($pushData));
So at the end, my websocket server is able to know which is the id of receiver using the JSON. But how will he know which is the connection of that user? In other words, I need to store websocket connections in an array that is indexed by the user id.
<?php
namespace RealTime;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\WampServerInterface;
class Pusher implements WampServerInterface, MessageComponentInterface{
private $clients;
public function onOpen(ConnectionInterface $conn) {
$this->clients[$conn->resourceId] = $conn;
// I need here to get the user_id received from browser while opening connection
}
public function onMessageEntry($entry) {
$entryData = json_decode($entry, true);
//This is not what I need (It sends to all users in array)
foreach ($this->clients as $key => $client) {
$client->send($entryData['message']);
}
}
public function onMessage(ConnectionInterface $from, $msg) {
echo $msg;
}
}
And the websocket server:
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use RealTime\Pusher;
$loop = React\EventLoop\Factory::create();
$pusher = new Pusher;
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', array($pusher, 'onMessageEntry'));
$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0');
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
)
),
$webSock
);
$loop->run();
?>
Questions:
How to send the logged in
user_id
from client side while opening connection.I need to have the value in websocket server so that I can index my array of clients with it ($client[user_id]=$conn
instead of$client[recourceId]=$conn
). I tried the javascript functionsend
but I don't know where to receive the sent data (evenonMessage
is not printing anything).Why the
onMessage
method is not executing evenMessageComponentInterface
implemented (Is it because I haveonMessageEntry
method +$pull->on('message', array($pusher, 'onMessageEntry'));
line of code?
Thank you.
回答1:
I was asked by "whiteletters in blankpapers" to contribute on this subject (sorry for being late).
Actually on my last try, I gave up on PHP WebSocket (it was so complicated to make this work) and started using SocketIO with nodeJS that solved my entire problem and could give me a functionnal simple Chat system.
回答2:
This is what I found and any suggestions to enhance this solution are welcome.
One can use the Ratchet SessionProvider. This will require using one of the Symfony Custom Session handlers as indicated. I use in the following code the PdoSessionHandler
.
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use YourDirectory\Pusher;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use \Ratchet\Session\SessionProvider;
$pusher = new Pusher;
$pdo = new PDO('mysql:host=localhost;dbname=community', 'root', null);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//This info is related to you db
$dbOptions = array(
'db_table' => 'session',
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',);
$loop = \React\EventLoop\Factory::create();
$context = new \React\ZMQ\Context($loop);
$pull = $context->getSocket(\ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', array($pusher, 'onMessageEntry'));
$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new SessionProvider(
new Ratchet\Wamp\WampServer(
$pusher
),new Handler\PdoSessionHandler($pdo,$dbOptions)
)
)
),
$webSock
);
$loop->run();
?>
Then my stub class will become:
public function onOpen(ConnectionInterface $conn) {
$this->clients[$conn->Session->get('current_user_id')] = $conn;
}
public function onMessageEntry($entry) {
$entryData = json_decode($entry, true);
$ReceiverConnection=$this->clients[$entryData['receiver_id']];
$ReceiverConnection->send($entryData['message']);
}
But before, I have added the user id to the session in Web server (in the controller that returns the initial page)
$user = $this->getUser();
$request->getSession()->set('current_user_id', $user->getId());
PS:
Moving to PdoSessionHandler can be done by implementing this (Symfony).
I still can't answer 2 but all the logic that can be put
onMessage
is now moved toonMessageEntry
which satisfies temporarly the needs.
回答3:
As an alternative way to make the association between your clientConnection
and his ID
you need to send a message using websockets just after opening the connection to your websocket server this message will contain your user id you will use it to index his connection object by his ID
in your array.
For the second question as I know the default websocket implementation is not working properly specialy with pubsub
protocol you need to use a websocket library for that I suggest to use AutobahnJS it's a good websocket library with a lot of wonderfull features.
来源:https://stackoverflow.com/questions/29576033/send-user-id-from-browser-to-websocket-server-while-opening-connection