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);
}
}
}
}
Очевидно, что вам, возможно, захочется провести более детальную проверку, но у вас должна получиться более подробная информация об этом.