Ratchet PHP WAMP-React/ZeroMQ-特定用户广播
注意:这与使用Ratchet PHP WAMP-React/ZeroMQ-特定用户广播,php,websocket,zeromq,ratchet,reactphp,Php,Websocket,Zeromq,Ratchet,Reactphp,注意:这与使用消息组件界面不同。我使用的是WampServerInterface,所以这个问题与该部分相关。我需要一个带有代码示例和解释的答案,因为我可以看到这对将来的其他人有帮助 尝试对单个用户进行循环推送 我正在使用Ratchet和ZeroMQ的WAMP部分,目前我有一个工作版本的 我正在尝试执行以下操作: zeromq服务器已启动并正在运行,准备记录订阅者和取消订阅者 用户通过websocket协议在浏览器中进行连接 启动一个循环,将数据发送给请求数据的特定用户 当用户断开连接时,该用户
消息组件界面
不同。我使用的是WampServerInterface
,所以这个问题与该部分相关。我需要一个带有代码示例和解释的答案,因为我可以看到这对将来的其他人有帮助
尝试对单个用户进行循环推送
我正在使用Ratchet和ZeroMQ的WAMP部分,目前我有一个工作版本的
我正在尝试执行以下操作:
- zeromq服务器已启动并正在运行,准备记录订阅者和取消订阅者
- 用户通过websocket协议在浏览器中进行连接
- 启动一个循环,将数据发送给请求数据的特定用户
- 当用户断开连接时,该用户数据的循环停止
php-bin/push-server.php
来运行它。出于调试目的,订阅和取消订阅将输出到此终端
$loop = React\EventLoop\Factory::create();
$pusher = 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, 'onMessage'));
$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\WebSocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
),
$webSock
);
$loop->run();
通过WebSocket发送数据的推进器
我省略了一些无用的东西,集中讨论了onMessage()
和onSubscribe()
方法
public function onSubscribe(ConnectionInterface $conn, $topic)
{
$subject = $topic->getId();
$ip = $conn->remoteAddress;
if (!array_key_exists($subject, $this->subscribedTopics))
{
$this->subscribedTopics[$subject] = $topic;
}
$this->clients[] = $conn->resourceId;
echo sprintf("New Connection: %s" . PHP_EOL, $conn->remoteAddress);
}
public function onMessage($entry) {
$entryData = json_decode($entry, true);
var_dump($entryData);
if (!array_key_exists($entryData['topic'], $this->subscribedTopics)) {
return;
}
$topic = $this->subscribedTopics[$entryData['topic']];
// This sends out everything to multiple users, not what I want!!
// I can't send() to individual connections from here I don't think :S
$topic->broadcast($entryData);
}
在循环中开始使用上述Pusher代码的脚本
这是我的问题-这是一个单独的php文件,希望将来可以集成到其他代码中,但目前我不确定如何正确使用它。我是否从会话中获取用户ID?我仍然需要从客户端发送它
// Thought sessions might work here but they don't work for subscription
session_start();
$userId = $_SESSION['userId'];
$loop = React\EventLoop\Factory::create();
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$i = 0;
$loop->addPeriodicTimer(4, function() use ($socket, $loop, $userId, &$i) {
$entryData = array(
'topic' => 'subscriptionTopicHere',
'userId' => $userId
);
$i++;
// So it doesn't go on infinitely if run from browser
if ($i >= 3)
{
$loop->stop();
}
// Send stuff to the queue
$socket->send(json_encode($entryData));
});
最后是要订阅的客户端js
结论
上述方法可行,但我确实需要弄清楚以下几点:
- 如何向单个用户发送单个消息?当他们访问在JS中启动websocket连接的页面时,我是否也应该启动在PHP(zeromq)中将内容推入队列的脚本?这就是我现在手动做的,感觉不对
- 当从JS订阅用户时,从会话中获取用户id并从客户端发送该id是不安全的。这可能是伪造的。请告诉我有一个更简单的方法,如果有,怎么做
{
"topic": "subject here",
"userId": "1",
"token": "dsah9273bui3f92h3r83f82h3"
}
JSON仍然是一个字符串,但它允许我传递更多数据来代替“主题”,PHP在另一端执行JSON\u decode()
也很简单。当然,您应该验证您是否确实收到了JSON,但这取决于您的实现
那么我在这里经过什么,为什么?
- 主题
- 用户ID
- 代币
$conn->remoteAddress
获取连接的IP,因此如果有人试图恶意连接,您可以阻止他们(记录他们或其他)
为什么这样做有效?
它之所以有效,是因为每次通过新连接时,唯一令牌确保任何用户都无法访问任何其他人的订阅数据
服务器
下面是我用来运行循环和事件处理程序的内容。我正在创建循环,做所有的装饰
{
"topic": "subject here",
"userId": "1",
"token": "dsah9273bui3f92h3r83f82h3"
}
$loop = Factory::create();
new IoServer(
new WsServer(
new WampServer(
new EventHandler($loop) // This is my class. Pass in the loop!
)
),
$webSock
);
$loop->run();
class EventHandler implements WampServerInterface, MessageComponentInterface
{
/**
* @var \React\EventLoop\LoopInterface
*/
private $loop;
/**
* @var array List of connected clients
*/
private $clients;
/**
* Pass in the react event loop here
*/
public function __construct(LoopInterface $loop)
{
$this->loop = $loop;
}
/**
* A user connects, we store the connection by the unique resource id
*/
public function onOpen(ConnectionInterface $conn)
{
$this->clients[$conn->resourceId]['conn'] = $conn;
}
/**
* A user subscribes. The JSON is in $subscription->getId()
*/
public function onSubscribe(ConnectionInterface $conn, $subscription)
{
// This is the JSON passed in from your JavaScript
// Obviously you need to validate it's JSON and expected data etc...
$data = json_decode(subscription->getId());
// Validate the users id and token together against the db values
// Now, let's subscribe this user only
// 5 = the interval, in seconds
$timer = $this->loop->addPeriodicTimer(5, function() use ($subscription) {
$data = "whatever data you want to broadcast";
return $subscription->broadcast(json_encode($data));
});
// Store the timer against that user's connection resource Id
$this->clients[$conn->resourceId]['timer'] = $timer;
}
public function onClose(ConnectionInterface $conn)
{
// There might be a connection without a timer
// So make sure there is one before trying to cancel it!
if (isset($this->clients[$conn->resourceId]['timer']))
{
if ($this->clients[$conn->resourceId]['timer'] instanceof TimerInterface)
{
$this->loop->cancelTimer($this->clients[$conn->resourceId]['timer']);
}
}
unset($this->clients[$conn->resourceId]);
}
/** Implement all the extra methods the interfaces say that you must use **/
}