【问题标题】:How to pass $_SESSION variables to a websocket server?如何将 $_SESSION 变量传递给 websocket 服务器?
【发布时间】:2014-06-07 11:47:21
【问题描述】:

我在网上搜索了很多,但没有找到有用的线索。

我有一个 websocket 服务器和一个 web 服务器在我的本地机器上一起运行。

当客户端使用浏览器 API 'new WebSocket("ws://localhost")' 连接到 websocket 服务器时,我需要将 $_SESSION 数据传递给它(请求使用反向代理发送到 websocket,它在收到带有“升级”标头的请求时知道它)。

关键是客户端成功连接到 ws 服务器,但我还需要使用 HTTP Web 服务器设置的 $_SESSION 变量恢复它们的 SESSION 数据。

其实我的情况是这样的(我用的是Ratchet库):

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\MyAppClassChat;

require dirname(__DIR__) . '/vendor/autoload.php';

$server = IoServer::factory(new HttpServer(new WsServer(new MyAppClass())), 8080);
$server->run();

MyAppClass 非常简单:

 <?php
namespace MyAppClass;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class MyAppClass implements MessageComponentInterface {

    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
            /* I would like to put recover the session infos of the clients here
               but the session_start() call returns an empty array ($_SESSION variables have been previuosly set by the web server)*/
        session_start();
        var_dump($_SESSION) // empty array...
        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numberOfReceivers = count($this->clients) -1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n", $from->resourceId, $msg, 
                                 $numberOfReceivers, $numberOfReceivers == 1 ? '' : 's');

        $this->clients->rewind();
        while ($this->clients->valid())
        {
            $client = $this->clients->current();
            if ($client !== $from) {
                $client->send($msg);
            }
            $this->clients->next();
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }
}

有没有办法用我的实际布局来做到这一点,或者我应该配置 apache 以便使用 mod_proxy_wstunnel 模块?

感谢帮助!!!

【问题讨论】:

    标签: php apache session websocket


    【解决方案1】:

    这可能看起来有点 hacky,但这是我能弄清楚如何做到这一点的唯一方法。

    假设您已经知道目录和 sessionId,您可以使用session_encode()session_decode() 直接从会话文件中读取数据,如下所示。我的会话文件以 sess_ 为前缀,其他人可能不是这样,所以请记住这一点。请注意,这将在从会话文件中提取变量后保存并恢复任何现有的 $_SESSION 数据:

    $contents = file_get_contents($sessionDir . 'sess_' . $sessionId);
    $sessionData = $this->decodeSession($contents);
    return $sessionData;
    
    private function decodeSession($sessionString)
    {
       $currentSession = session_encode();
       foreach ($_SESSION as $key => $value){
         unset($_SESSION[$key]);
       }
       session_decode($sessionString);
       $sessionVariables = $_SESSION;
       foreach ($_SESSION as $key => $value){
         unset($_SESSION[$key]);
       }
       session_decode($currentSession);
       return $sessionVariables;
    }
    

    【讨论】:

      【解决方案2】:

      正如其他 StackOverflow 答案所示(Ratchet without Symfony sessionStarting a session within a ratchet websocket connection),无法在 Apache 和 Ratchet 进程之间直接共享 $_SESSION 变量。但是,可以启动与 Apache 服务器的会话,然后在 Ratchet 代码中访问会话 cookie。

      Apache 服务器的 index.html 启动会话:

      <?php
      // Get the session ID.
      $ses_id = session_id();
      if (empty($ses_id)) {
          session_start();
          $ses_id = session_id();
      }
      ?><!DOCTYPE html> ...
      

      Ratchet MessageComponentInterface 代码访问会话令牌:

      public function onMessage(ConnectionInterface $from, $msg) {
          $sessionId = $from->WebSocket->request->getCookies()['PHPSESSID'];
          # Do stuff with the token...
      }
      

      一旦两个服务器都知道用户的会话令牌,它们就可以使用该令牌通过 MySQL 数据库共享信息(我就是这样做的):

          # Access session data from a database:
          $stmt = $this->mysqli->prepare("SELECT * FROM users WHERE cookie=?");
          $stmt->bind_param('s', $sessionId);
          $stmt->execute();
          $result = $stmt->get_result();
      

      或者,您可以进行更奇特的进程间通信:

          # Ratchet server:
          $opts = array(
              'http'=>array(
                  'method'=>'GET',
                  'header'=>"Cookie: PHPSESSID=$sessionId\r\n"
              )
          );
          $context = stream_context_create($opts);
          $json = file_get_contents('http://localhost:80/get_session_info.php', false, $context);
          $session_data = json_decode($json);
      
          # Apache server's get_session_info.php
          # Note: restrict access to this path so that remote users can't dump
          # their own session data.
          echo json_encode($_SESSION);
      

      【讨论】:

      • 真的很聪明,但是你的意思是“用 cookie 向 Apache 进程询问用户?”你怎么能做到这一点?我的意思是,如果我不使用数据库进行会话,我需要以某种方式让 WebSocket 服务器要求 PHP 使用会话 ID 检索当前发送者的会话。我认为这将是错误的方式,对吗? public function onMessage(ConnectionInterface $from, $msg) { $sessionId = $from-&gt;WebSocket-&gt;request-&gt;getCookies()['PHPSESSID']; session_id($sessionId); session_start(); echo $_SESSION['somekey']; }这个靠谱吗?
      • 我认为你不能在 websocket 服务器内部调用 session_start();只有 Apache 服务器可以设置会话令牌,因为 set cookie 命令必须放在网页的标题中。一旦两台服务器都拥有会话令牌,您就可以自行决定如何使用会话令牌在进程间通信会话数据。由于 Apache 和 Ratchet 都是服务器,因此您可以通过自行抓取将数据从一个进程“提供”到另一个进程。
      • 对不起,我不明白,我明白我不能像我想象的那样使用 sesssion_start() ,但是要让 Apache 和 Ratchet 相互通信,我应该实现什么?从 Ratchet 调用并寻址到 Apache 的 Web 服务,以便根据会话标识符或什么获取正确的数据?感谢您的关注和关注!
      • 使用 MySQL 会话是否比使用简单的 PHP 会话更可靠和可扩展?
      • 是的,这将起作用:“从 Ratchet 调用并寻址到 Apache 以根据会话标识符获取正确数据的 Web 服务”。如果您没有数据库,则可能不值得为 Apache 和 Ratchet 设置数据库来共享数据。如果您已经有一个数据库,那么存储和共享会话信息非常容易。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-13
      • 1970-01-01
      • 1970-01-01
      • 2021-08-05
      • 2021-04-01
      • 2014-09-28
      • 2015-01-15
      相关资源
      最近更新 更多