Ratchet PHP Websockets: частный обмен сообщениями (контроль, кому сообщения отправляются)

Есть еще один вопрос. Я начинаю привыкать к тому, как работает websocket. Мне даже удалось реализовать связь между доменами. Но сейчас мне еще предстоит пройти еще одну веху.

Вот фрагмент из моей текущей реализации

 public function onMessage(ConnectionInterface $conn, $msg)
{
     $msgjson = json_decode($msg);
     $tag = $msgjson->tag;
     global $users; 

     if($tag == "[msgsend]")
     {

            foreach($this->clients as $client)
            {
                  $client->send($msg);    
            }
     }
     else if($tag == "[bye]")
     {

         foreach($this->clients as $client)
         {
              $client->send($msg);    
         }

         foreach($users as $key => $user)
         {
             if($user->name == $msgjson->uname)
             {
                unset($users[$key]); 
             }
         }

         $this->clients->detach($conn);
     }
     else if($tag == "[connected]")
     {
         //store client information
         $temp = new Users();
         $temp->name = $msgjson->uname;
         $temp->connection = $conn;
         $temp->timestamp = new \DateTime();



         $users[] = $temp;





          usort($users, array($this, "cmp"));


         //send out messages
          foreach($this->clients as $client)
         {
              $client->send($msg);    
         }           

     }
     else if($tag == "[imalive]")
     {
         //update user timestamp who sent [imalive]
         global $users;

          foreach($users as $user)
             {
                if($msgjson->uname == $user->name)
                {
                        $user->timestamp = new \DateTime(); 
                }
             }

     }   

}

Теперь мой вопрос Как мы видим, в функции onMessage() и учебных пособиях, которые я сделал, я знаю, как читать и анализировать данные JSON, понимать смысл сообщений, сообщать, от кого приходит сообщение ($conn).....

Но, скажем, когда я отправляю сообщение в пакете JSON, я хочу включить псевдонимы, от которых исходит сообщение, а также от того, кому оно предназначено. Это позволит мне реализовать частный обмен мгновенными сообщениями как в моей социальной сети, которую я создаю, так и в чатах.

Вместо циклической отправки сообщений всем подключенным клиентам я хочу отправлять сообщения только определенным. Я знаю, что у клиентов есть свойство ($this->$client->resourceID или что-то в этом роде), но я не уверен, как включить его в качестве решения. Я также хочу, чтобы пользователи поддерживали соединение, когда они переходили на разные страницы веб-сайта, и даже после обновления все еще могли продолжать обмен сообщениями. Я предполагаю, что каждое обновление отключает клиента. Поэтому мне нужно, чтобы сервер мог каждый раз сообщать, кто есть кто и откуда приходят сообщения и куда они направляются.

Но да, личное сообщение. Я не хочу посылать ненужные сообщения всем или непреднамеренным целям. Как я могу сделать это? Я надеюсь, что мой вопрос имеет смысл. Благодарю.

1 ответ

Возможность уникальной идентификации пользователей, которые подключаются к вашему серверу WebSocket, а затем возможность ориентироваться на этих пользователей, особенно при отправке сообщений, должна начинаться с onOpen обратный вызов, когда соединение фактически согласовывается с сервером.

В вашем onOpen метод, у вас должен быть какой-то способ уникальной идентификации пользователя в вашей системе с помощью некоторого идентификатора пользователя, который глобально хранится в вашей базе данных или в хранилище постоянных данных. Поскольку соединение устанавливается по HTTP, у вас есть доступ к HTTP-запросу через $conn->WebSocket->request, который является GuzzleHttp объект, содержащий информацию HTTP-запроса клиента. Вы можете использовать это, чтобы получить cookie, например, который содержит некоторые данные или токен идентификатора пользователя, которые вы можете сравнить с вашей базой данных, чтобы выяснить, кто пользователь, а затем сохранить его как свойство $client объект.

А теперь давайте представим, что вы пишете обычный скрипт PHP, в котором вы выполняете аутентификацию пользователя по HTTP и сохраняете идентификатор пользователя в сеансе. Этот сеанс устанавливает cookie на компьютере клиента (по умолчанию имя cookie - это имя сеанса, которое обычно PHPSESSIDесли вы не измените его), содержащий идентификатор сеанса. Этот идентификатор сеанса может использоваться на вашем сервере WebSocket для доступа к хранилищу сеансов так же, как вы это обычно делаете в PHP.

Вот простой пример, где мы ожидаем, что печенье с именемPHPSESSIDиз запроса на захват идентификатора сеанса из cookie.

public function onOpen(ConnectionInterface $conn) {
    // extract the cookie header from the HTTP request as a string
    $cookies = (string) $conn->WebSocket->request->getHeader('Cookie');
    // look at each cookie to find the one you expect
    $cookies = array_map('trim', explode(';', $cookies));
    $sessionId = null;
    foreach($cookies as $cookie) {
        // If the string is empty keep going
        if (!strlen($cookie)) {
            continue;
        }
        // Otherwise, let's get the cookie name and value
        list($cookieName, $cookieValue) = explode('=', $cookie, 2) + [null, null];
        // If either are empty, something went wrong, we'll fail silently here
        if (!strlen($cookieName) || !strlen($cookieValue)) {
            continue;
        }
        // If it's not the cookie we're looking for keep going
        if ($cookieName !== "PHPSESSID") {
            continue;
        }
        // If we've gotten this far we have the session id
        $sessionId = urldecode($cookieValue);
        break;
    }
    // If we got here and $sessionId is still null, then the user isn't logged in
    if (!$sessionId) {
        return $conn->close(); // close the connection - no session!
    }
}

Теперь, когда у вас есть$sessionIdВы можете использовать его для доступа к хранилищу сеансов для этого сеанса, извлекать информацию о сеансах на сервер WebSocket и сохранять ее как свойство объекта клиентского подключения.$conn,

Итак, продолжая из приведенного выше примера, давайте добавим этот код вonOpenметод.

public function onOpen(ConnectionInterface $conn) {
    $conn->session = $this->methodToGetSessionData($sessionId);
    // now you have access to things in the session
    $this->clinets[] = $conn;
}

Теперь вернемся к вашему примеру, где вы хотите отправить сообщение только одному пользователю. Давайте предположим, что в сеансе хранятся следующие свойства, которые теперь доступны из объекта клиентского соединения...

$conn->session->userName = 'Bob';
$conn->session->userId   = 1;

Допустим, Боб хочет отправить сообщение Джейн. На ваш WS-сервер приходит запрос с чем-то вроде {"from":1,"to":2,tag:"[msgsend]"} где toа такжеfrom свойства этого JSON - это, в основном, идентификаторы пользователя, от которого отправлено сообщение, и пользователя, которому сообщение должно быть отправлено, соответственно. Давайте предположим, что Джейн userId = 2 для этого примера.

public function onMessage(ConnectionInterface $conn, $msg) {
    $msgjson = json_decode($msg);
    $tag = $msgjson->tag;
    if ($tag == '[msgsend]') {
        foreach($this->clients as $client) {
            // only send to the designated recipient user id
            if ($msgjson->to == $client->session->userId) {
                $client->send($msg);    
            }
        }
    }
}

Очевидно, что вам, возможно, захочется провести более детальную проверку, но у вас должна получиться более подробная информация об этом.

Другие вопросы по тегам