Я пытаюсь настроить сервер веб-сокетов через Thruway, который может управлять несколькими группами. Что-то вроде приложения чата, где каждый клиент может подписаться на одного или нескольких одновременно и транслировать сообщения на всю комнату чата. Мне удалось сделать это с древней версией Ratchet, но, поскольку она работает не очень гладко, я хотел переключиться на Thruway. К сожалению, я не могу найти что-либо для управления группами. Пока у меня есть следующее как websocket-manager, и клиенты используют текущую версию Autobahn | js (18.x).
У кого-нибудь есть подсказка, можно ли управлять группами подписок с помощью чего-то вроде следующего?
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Thruway\Peer\Router;
use Thruway\Transport\RatchetTransportProvider;
$router = new Router();
$router->addTransportProvider(new RatchetTransportProvider("0.0.0.0", 9090));
$router->start();
С ThruWay все немного отличается от старого Ratchet. Во-первых, Thruway не является сервером WAMP. Это просто роутер. Так что у него нет экземпляра сервера, как у старого Rathcet, который позволяет обернуть все функции сервера. Но он получит только пакет сообщений и route
их на другие сеансы в той же области в зависимости от их подписки. Если вы когда-либо использовали socket.io, идея области похожа на различные подключения, поэтому вы можете ограничить свои сеансы или подключения одним пространством имен или разделить функциональность различных экземпляров сокетов, таких как администрация, посетители и т. Д.
На стороне клиента с autobahn (последняя версия), когда вы подписываетесь на тему, а затем публикуете в этой теме, thruway автоматически обнаруживает подписчиков темы и отправляет им сообщения в той же области. Но в старых храповиках вам нужно обрабатывать это вручную, сохраняя массив доступных каналов, и добавлять пользователей к каждому каналу, когда они подписываются, а также рассылать сообщения этим пользователям в теме, просматривая их. Это было действительно больно.
Если вы хотите использовать вызовы RPC на стороне сервера и не хотите включать некоторые из ваших вещей на стороне клиента, вы все равно можете использовать класс internalClient на стороне сервера. Концептуально Внутренний клиент — это еще один сеанс, который подключается к вашему клиенту через клиент и обрабатывает некоторые функции внутри, не подвергая других клиентов. Он получает пакеты сообщений и делает что-то в нем, а затем возвращает результат обратно запрошенному клиентскому соединению. Мне потребовалось некоторое время, чтобы понять, как это работает, но однажды я понял, что идея позади имеет больше смысла.
так немного кода, чтобы объяснить лучше,
В вашем экземпляре маршрутизатора вам нужно будет добавить модуль (обратите внимание, что в примерах пакетов voxys / thruway нет ничего странного во внутреннем клиенте)
server.php
require __DIR__ . "/../bootstrap.php";
require __DIR__ . '/InternalClient.php';
$port = 8080;
$output->writeln([
sprintf('Starting Sockets Service on Port [%s]', $port),
]);
$router = new Router();
$router->registerModule(new RatchetTransportProvider("127.0.0.1", $port)); // use 0.0.0.0 if you want to expose outside world
// common realm ( realm1 )
$router->registerModule(
new InternalClient() // instantiate the Socket class now
);
// administration realm (administration)
// $router->registerModule(new \AdminClient());
$router->start();
Это инициализирует маршрутизатор Thruway и подключит к нему экземпляр внутреннего клиента. Теперь в файле InternalClient.php вы сможете получить доступ как к текущему маршруту, так и к текущим подключенным клиентам. В приведенном ими примере маршрутизатор не является частью экземпляра, поэтому вы застреваете только со свойством идентификатора сеанса новых соединений.
InternalClient.php
<?php
use Thruway\Module\RouterModuleInterface;
use Thruway\Peer\Client;
use Thruway\Peer\Router;
use Thruway\Peer\RouterInterface;
use Thruway\Logging\Logger;
use React\EventLoop\LoopInterface;
class InternalClient extends Client implements RouterModuleInterface
{
protected $_router;
/**
* Contructor
*/
public function __construct()
{
parent::__construct("realm1");
}
/**
* @param RouterInterface $router
* @param LoopInterface $loop
*/
public function initModule(RouterInterface $router, LoopInterface $loop)
{
$this->_router = $router;
$this->setLoop($loop);
$this->_router->addInternalClient($this);
}
/**
* @param \Thruway\ClientSession $session
* @param \Thruway\Transport\TransportInterface $transport
*/
public function onSessionStart($session, $transport)
{
// TODO: now that the session has started, setup the stuff
echo "--------------- Hello from InternalClient ------------\n";
$session->register('com.example.getphpversion', [$this, 'getPhpVersion']);
$session->subscribe('wamp.metaevent.session.on_join', [$this, 'onSessionJoin']);
$session->subscribe('wamp.metaevent.session.on_leave', [$this, 'onSessionLeave']);
}
/**
* Handle on new session joined.
* This is where session is initially created and client is connected to socket server
*
* @param array $args
* @param array $kwArgs
* @param array $options
* @return void
*/
public function onSessionJoin($args, $kwArgs, $options) {
$sessionId = $args && $args[0];
$connectedClientSession = $this->_router->getSessionBySessionId($sessionId);
Logger::debug($this, 'Client '. $sessionId. ' connected');
}
/**
* Handle on session left.
*
* @param array $args
* @param array $kwArgs
* @param array $options
* @return void
*/
public function onSessionLeave($args, $kwArgs, $options) {
$sessionId = $args && $args[0];
Logger::debug($this, 'Client '. $sessionId. ' left');
// Below won't work because once this event is triggered, client session is already ended
// and cleared from router. If you need to access closed session, you may need to implement
// a cache service such as Redis to access data manually.
//$connectedClientSession = $this->_router->getSessionBySessionId($sessionId);
}
/**
* RPC Call messages
* These methods will run internally when it is called from another client.
*/
private function getPhpVersion() {
// You can emit or broadcast another message in this case
$this->emitMessage('com.example.commonTopic', 'phpVersion', array('msg'=> phpVersion()));
$this->broadcastMessage('com.example.anotherTopic', 'phpVersionRequested', array('msg'=> phpVersion()));
// and return result of your rpc call back to requester
return [phpversion()];
}
/**
* @return Router
*/
public function getRouter()
{
return $this->_router;
}
/**
* @param $topic
* @param $eventName
* @param $msg
* @param null $exclude
*/
protected function broadcastMessage($topic, $eventName, $msg)
{
$this->emitMessage($topic, $eventName, $msg, false);
}
/**
* @param $topic
* @param $eventName
* @param $msg
* @param null $exclude
*/
protected function emitMessage($topic, $eventName, $msg, $exclude = true)
{
$this->session->publish($topic, array($eventName), array('data' => $msg), array('exclude_me' => $exclude));
}
}
Несколько вещей, чтобы отметить в приведенном выше примере кода,
— Чтобы получить сообщение в теме, на стороне клиента необходимо подписаться на эту тему.
— Внутренний клиент может публиковать / излучать / транслировать любую тему без подписки в той же области.
— функции широковещания / передачи не являются частью первоначального сквозного подхода, я придумал, чтобы сделать публикации немного легче с моей стороны. emit будет отправлять пакет сообщений всем, кто подписался на тему, кроме отправителя. Трансляция с другой стороны не исключает отправителя.
Я надеюсь, что эта информация немного поможет понять концепцию.
Других решений пока нет …