Javascript 使用symfony 2.6是否有一种干净的方法来设置websocket

Javascript 使用symfony 2.6是否有一种干净的方法来设置websocket,javascript,ajax,symfony,websocket,Javascript,Ajax,Symfony,Websocket,我用symfony2开发了一个网站。 直到现在,它还没有使用ajax。当您发布新评论时,页面已刷新 我添加了一个ajax层,因此表单提交时无需刷新 每次我发布一篇新文章,我希望所有在线用户都能在他们的时间线中看到这篇文章 这就是我现在所做的: 我创建了一个事件NewPostAdded和一个订阅者呈现html帖子(希望我将它发送给客户端,客户端将$('.timeline')。prepend(post)) 我正在寻找一种实现symfony2命令的方法,该命令将启动websocket服务器。我的订户将

我用symfony2开发了一个网站。 直到现在,它还没有使用ajax。当您发布新评论时,页面已刷新

我添加了一个ajax层,因此表单提交时无需刷新

每次我发布一篇新文章,我希望所有在线用户都能在他们的时间线中看到这篇文章

这就是我现在所做的:

我创建了一个事件NewPostAdded和一个订阅者呈现html帖子(希望我将它发送给客户端,客户端将$('.timeline')。prepend(post))

我正在寻找一种实现symfony2命令的方法,该命令将启动websocket服务器。我的订户将能够将post+数据(它是公共的吗?其他允许查看它的用户id列表)推送到该服务器。该服务器将有一个在线用户列表,如果帖子是公开的,它将推送给其他所有人,它将推送给正确的在线用户

在使用symfony2.1+redis+nodejs+socket.io之前,我已经这样做了,但它显然无法维护,因为这个网站并不是面向大受众的,所以我想保持简单

以下是我的两个问题:

  • 是否有一个捆绑包使think simple提供了一种“一体式”的简单方式,用income events listeners(从symfony接收帖子)和event sender(向用户发送帖子)、add event listeners客户端(准备使用资产添加以便能够对客户端进行编码)对服务器消息做出反应

  • 是否有一种方法可以在客户端使用用户id以外的其他内容来“验证”websocket服务器上的用户,以避免用户更改客户端代码中的id以接收他们不应该查看的帖子


是的,您可以使用php web socket Ratchet“”,您可以将其用作编写器包

您应该先创建一个服务应用程序,然后再创建一个将运行套接字应用程序的控制台命令

代替userId,您应该生成一个秘密令牌,比如userId的md5散列+一些用于检查套接字连接是否可信的密钥

更新:

composer.json

...
"cboden/ratchet": "0.3.*",
...
然后您应该创建一个新的捆绑包,比如“WebSocketBundle”

  • 创建服务应用程序 WebSocketBundle/WebSocket/WebSocketApplication.php
  • 控制台命令:

  • 所以我可以在symfony2中使用这个包,将它添加到我的composer.json中?你有任何在symfony2中实现这个包的例子吗?是的,但不能在这里发布完整的代码案例NDA,但我会尝试向你展示一些关键点Cellent,我现在就要尝试!感谢您的帮助查看eventsource(sse),从“裸”php使用它比从套接字使用要容易得多,并且可以使用与http相同的基本例程随时推出。它无法接收,但由于这一限制,实现起来非常简单,而且您可以随时使用ajax从客户端发送。。。
        namespace MyApp\WebSocketBundle\WebSocket;
    
        use Ratchet\ConnectionInterface;
        use Ratchet\MessageComponentInterface;
    
        class WebSocketApplication implements MessageComponentInterface
        {
            protected $container;
            protected $clients;
            protected $redis;
    
            public function __construct($container)
            {
                $this->clients = [];
                $this->container = $container;
                $this->redis = $container->get('snc_redis.something'); // your redis service
            }
    
            public function onMessage(ConnectionInterface $from, $msg)
            {
                $messageData = $this->decodeJSONAndCheckMessage($from, $msg);
    
                // here you must pass a token in messageData and implement own function to check is the token valid 
                $loginResult = $this->userLogin($from, $messageData);
    
                if ($loginResult instanceof Success) { // my custom success class message
                    $this->handleMessage($from, $messageData);
                } else {
                    $this->onError($from, new \Exception('Cannot login a user.'));
                }
            }
    
            // some strategy wrapper
            private function handleMessage($from, $messageData)
            {
                $message = $messageData->message;
                if (method_exists($this, $message)) {
                    try {
                        $this->$message($from, $messageData);
                    } catch (Exception $ex) {
                        $this->onError($from, $ex);
                    }
                } else {
                    $this->onError($from, new \Exception(sprintf('Unknown method "%s"', $message)));
                }
            }
    
            // you can use here post new message action
            private function eventStartSomething($from, $messageData)
            {
                if (!$messageData->somethingId) {
                    $this->onError($from, new \Exception('Bad parameters'));
                    return;
                }
                $scope = [];
                $scope['clients'][$from->resourceId] = null;
    
                // I need socket for something only limited amount of time, you can implement here own logic
                $this->redis->setex($messageData->somethingId, 600, serialize($scope));
    
                $this->clients[$from->resourceId]['scope'] = $messageData->eventId;
                $this->logMessage($from, 'started new something with Id: ' . $messageData->somethingId);
                $from->send($this->getResultOKMessage());
            }
    
            private function eventGetSomething($from, $messageData)
            {
                $scopeKey = $this->redis->get($messageData->somethingId);
                if (!$scopeKey) {
                    $this->onError($from, new \Exception('Bad or expired something ' . $messageData->somethingId));
                    return;
                }
                if (!$this->checkForClientInScope($from->resourceId, $messageData->eventId)) {
                    if ($this->assignClientToScope($from->resourceId, $messageData->eventId)) {
                        $this->sendMessageToScope($from, $messageData->eventId, $this->getScopeWatchMessage($from, $messageData->eventId));
                        $from->send($this->getScopeWatchMessage($from, $messageData->eventId));
                    }
                }
            }
    
            private function assignClientToScope($clienResourseId, $scopeId)
            {
                $result = false;
                $scopeKey = $this->redis->get($scopeId);
                if (!$scopeKey) {
                    return $result;
                }
                $scope = unserialize($scopeKey);
                if (!array_key_exists($clienResourseId, $scope['clients'])) {
    
                // I need socket for something only limited amount of time, you can implement here own logic
                    $this->redis->setex($scopeId, 600, serialize($scope));
    
                    if (array_key_exists($clienResourseId, $this->clients)) {
                        $this->clients[$clienResourseId]['scope'] = $scopeId;
                    }
                    $result = true;
                }
                return $result;
            }
    
            private function sendMessageToScope($from, $scopeId, $message)
            {
                $scopeKey = $this->redis->get($scopeId);
                if (!$scopeKey) {
                    $this->onError($from, new \Exception('Bad or expired event ' . $scopeId . ' for sending message'));
                    return;
                }
                $scope = unserialize($scopeKey);
    
                foreach ($scope['clients'] as $clientResourceId => $remoteAddress) {
                    if (array_key_exists($clientResourceId, $this->clients) &&
                            $this->clients[$clientResourceId]['connection'] != $from) {
                        $this->clients[$clientResourceId]['connection']->send($message);
                    }
                }
            }
    
            public function onClose(ConnectionInterface $conn)
            {
                if (isset($this->clients[$conn->resourceId]['scope'])) {
                    $scopeId = $this->clients[$conn->resourceId]['scope'];
                    $this->removeClientFromScope($conn->resourceId);
                    $this->sendMessageToScope($conn, $scopeId, $this->getScopeWatchMessage($conn, $scopeId));
                }
                unset($this->clients[$conn->resourceId]);
                $this->logMessage($conn, 'Connection closed.');
            }
    
            public function onError(ConnectionInterface $conn, \Exception $e)
            {
                echo date("Y-m-d H:i:s") . ":" . "WebSocket error::" . $e->getMessage() . " resourceId:" . $conn->resourceId . ". remoteAddress:" . $conn->remoteAddress . "\n";
                $conn->send($this->getErrorMessage($e->getMessage()));
            }
    
            public function onOpen(ConnectionInterface $conn)
            {
                $this->clients[$conn->resourceId]['connection'] = $conn;
                $this->logMessage($conn, 'New connection.');
            }
        }
    
    use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    use Symfony\Component\Console\Input\InputOption;
    use Ratchet\Server\IoServer;
    use Ratchet\Http\HttpServer;
    use Ratchet\WebSocket\WsServer;
    use Snc\RedisBundle\Session\Storage\Handler\RedisSessionHandler;
    use Ratchet\Session\SessionProvider;
    use MyApp\WebSocketBundle\WebSocket\WebSocketApplication;
    
    class ListenCommand extends ContainerAwareCommand
    {
        protected function configure()
        {
            $this->setName('myapp:websocket:listen')
                ->setDescription('Listen for websocket requests ')
                ->addOption('port', 'p', InputOption::VALUE_REQUIRED, 'The port to listen on', 8000)
                ->addOption('interface', 'i', InputOption::VALUE_REQUIRED, 'The interface to listen on', '0.0.0.0');
        }
    
        protected function execute(InputInterface $input, OutputInterface $output)
        {
            $redis = $this->getContainer()->get('snc_redis.default');
            $application = new WebSocketApplication($this->getContainer());
            $server = IoServer::factory(
                new HttpServer(
                    new WsServer(
                        new SessionProvider(
                            $application,
                            new RedisSessionHandler($redis)
                        )
                    )
                ),
                $input->getOption('port'),
                $input->getOption('interface')
            );
            echo "Listening on: ".$input->getOption('interface').":".$input->getOption('port')."\n";
            $server->run();
    
        }
    }