打开连接时将用户 ID 从浏览器发送到 websocket 服务器
Send user ID from browser to websocket server while opening connection
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).
我的最终目标是使用 Ratchet 构建一个一对一的私人聊天应用程序。一切正常,只是我无法向特定用户发送消息。
每个登录用户在访问网站的安全区域时都会连接到 websocket 服务器:
$(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);
};
});
当用户在聊天框中写消息时,消息会通过 AJAX 发送到 Web 服务器,然后使用 ZeroMQ 推送到 Websocket。在控制器中:
// 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));
所以最后,我的 websocket 服务器能够使用 JSON 知道哪个是接收者的 ID。但是他怎么知道哪个是那个用户的连接呢?换句话说,我需要将 websocket 连接存储在一个由用户 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;
}
}
和 websocket 服务器:
<?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();
?>
问题:
如何在打开时从客户端发送登录的 user_id
connection.I 需要在 websocket 服务器中具有值,以便我可以用它索引我的客户端数组($client[user_id]=$conn
而不是 $client[recourceId]=$conn
)。我尝试了 javascript 函数 send
但我不知道从哪里接收发送的数据(即使 onMessage
也没有打印任何东西)。
为什么 onMessage
方法甚至 MessageComponentInterface
都没有执行(是因为我有 onMessageEntry
方法 + $pull->on('message', array($pusher, 'onMessageEntry'));
行代码吗?
谢谢。
这是我的发现,欢迎提出任何改进此解决方案的建议。
可以使用棘轮 SessionProvider。这将需要使用指定的 Symfony 自定义会话处理程序之一。我在下面的代码中使用 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();
?>
那么我的存根class会变成:
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']);
}
但之前,我已经将用户 ID 添加到 Web 服务器的会话中(在 returns 初始页面的控制器中)
$user = $this->getUser();
$request->getSession()->set('current_user_id', $user->getId());
PS:
移动到 PdoSessionHandler 可以通过实现 this (Symfony) 来完成。
2我还是答不上来,不过现在把可以放在onMessage
的逻辑全部移到onMessageEntry
,暂时满足需求
作为在您的 clientConnection
和他的 ID
之间建立关联的替代方法,您需要在打开与您的 websocket 服务器的连接后立即使用 websockets 发送消息,此消息将包含您的用户id 你将使用它通过你的数组中的他的 ID
来索引他的连接对象。
对于第二个问题,我知道默认的 websocket 实现不能正常工作,特别是 pubsub
协议,你需要使用一个 websocket 库,我建议使用 AutobahnJS
这是一个很好的 websocket具有许多精彩功能的图书馆。
实际上,在我最后一次尝试时,我放弃了 PHP WebSocket(使这项工作变得如此复杂)并开始将 SocketIO 与 nodeJS 一起使用,这解决了我的整个问题并且可以给我一个功能简单的聊天系统.
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).
我的最终目标是使用 Ratchet 构建一个一对一的私人聊天应用程序。一切正常,只是我无法向特定用户发送消息。
每个登录用户在访问网站的安全区域时都会连接到 websocket 服务器:
$(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);
};
});
当用户在聊天框中写消息时,消息会通过 AJAX 发送到 Web 服务器,然后使用 ZeroMQ 推送到 Websocket。在控制器中:
// 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));
所以最后,我的 websocket 服务器能够使用 JSON 知道哪个是接收者的 ID。但是他怎么知道哪个是那个用户的连接呢?换句话说,我需要将 websocket 连接存储在一个由用户 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;
}
}
和 websocket 服务器:
<?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();
?>
问题:
如何在打开时从客户端发送登录的
user_id
connection.I 需要在 websocket 服务器中具有值,以便我可以用它索引我的客户端数组($client[user_id]=$conn
而不是$client[recourceId]=$conn
)。我尝试了 javascript 函数send
但我不知道从哪里接收发送的数据(即使onMessage
也没有打印任何东西)。为什么
onMessage
方法甚至MessageComponentInterface
都没有执行(是因为我有onMessageEntry
方法 +$pull->on('message', array($pusher, 'onMessageEntry'));
行代码吗?
谢谢。
这是我的发现,欢迎提出任何改进此解决方案的建议。
可以使用棘轮 SessionProvider。这将需要使用指定的 Symfony 自定义会话处理程序之一。我在下面的代码中使用 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();
?>
那么我的存根class会变成:
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']);
}
但之前,我已经将用户 ID 添加到 Web 服务器的会话中(在 returns 初始页面的控制器中)
$user = $this->getUser();
$request->getSession()->set('current_user_id', $user->getId());
PS:
移动到 PdoSessionHandler 可以通过实现 this (Symfony) 来完成。
2我还是答不上来,不过现在把可以放在
onMessage
的逻辑全部移到onMessageEntry
,暂时满足需求
作为在您的 clientConnection
和他的 ID
之间建立关联的替代方法,您需要在打开与您的 websocket 服务器的连接后立即使用 websockets 发送消息,此消息将包含您的用户id 你将使用它通过你的数组中的他的 ID
来索引他的连接对象。
对于第二个问题,我知道默认的 websocket 实现不能正常工作,特别是 pubsub
协议,你需要使用一个 websocket 库,我建议使用 AutobahnJS
这是一个很好的 websocket具有许多精彩功能的图书馆。
实际上,在我最后一次尝试时,我放弃了 PHP WebSocket(使这项工作变得如此复杂)并开始将 SocketIO 与 nodeJS 一起使用,这解决了我的整个问题并且可以给我一个功能简单的聊天系统.