Я новичок в websockets. Ive только что завершил мой первый многоцелевой сервер сокетов, который обслуживает 3 типа сообщений в одном и том же сокете. Предполагаемое использование этого сервера выглядит следующим образом:
chat messages real time market data user specific alerts
Первые две функции отлично работают, и я полностью счастлив, однако я испытываю полную потерю, когда дело касается идентификации и связывания одноранговых узлов в соединении с конкретными учетными записями, поэтому я могу обслуживать ожидающие уведомления о предупреждениях для конкретных пользователей.
в основном, я сохраняю предупреждения в таблице базы данных следующим образом:
Table: uc_notifications account(varchar50) | message(varchar255) | id(primary,AI) | seen(default 0)
Я хочу хранить сверстников в таблице так:
uc_notification_peers account(varchar50) | peer_id(unique,varchar100) | id(primary,AI)
В принципе, проблема, с которой я сталкиваюсь, заключается в хранении информации о сверстнике в db и ее извлечении, чтобы я мог отправить сообщение выбранному одноранговому узлу при чтении таблицы уведомлений. Все остальное в скрипте работало.
Первое, что я пробовал, – это передать имя учетной записи в websocket, используя onopen. это приведет к сбою в сети.
Следующее, что я попробовал, – это доступ к сеансу пользователя через глобальные переменные, относящиеся к ip-адресу. тоже не работало, и в результате были получены сообщения, полученные всеми учетными записями на одном ip (я тестировал на localhost во всех трех браузерах).
Последнее, что я пробовал, было первым методом. я установил тайм-аут в течение 2 секунд в открытом событии, а затем отправил сообщение учетной записи. я смог наконец сохранить сверстник в базе данных! но, к моему огорчению, когда я проверил базу данных, он сказал («Идентификатор ресурса №6»). Я попробовал кастинг массива $ clients в глобальные переменные, но каждый раз был пустым. Я немного погубил, а затем попробовал сериализацию массива. работает, пока я не доберусь до той части, где я отправлю сообщение уникальному партнеру.
вот функция отправки сообщения
function send_message_single_client($msg,$client) { @socket_write($client,$msg,strlen($msg)); return true; }
вот пример того, где я использую эту функцию, чтобы нажимать последние 100 сообщений в чат-банке нового однорангового узла.
if (in_array($socket, $changed)) { $socket_new = socket_accept($socket); $clients[] = $socket_new; $header = socket_read($socket_new, 1024); perform_handshaking($header, $socket_new, $host, $port); socket_getpeername($socket_new, $ip); $found_socket = array_search($socket, $changed); //need to push last 100 messages to new peer. $query = $mysqli->query("SELECT * FROM (SELECT * FROM uc_chat_msg WHERE `hidden`='0' ORDER BY `id` DESC LIMIT 100) as last100 ORDER BY id"); while($row = $query->fetch_assoc()) { $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>security($row["username"]), 'message'=>security($row["message"]), 'color'=>security($row["color"])))); send_message_single_client($response_text, $socket_new);//send old messages to the new client } $query->close(); unset($changed[$found_socket]); }
сif (in_array($socket, $changed)) { $socket_new = socket_accept($socket); $clients[] = $socket_new; $header = socket_read($socket_new, 1024); perform_handshaking($header, $socket_new, $host, $port); socket_getpeername($socket_new, $ip); $found_socket = array_search($socket, $changed); //need to push last 100 messages to new peer. $query = $mysqli->query("SELECT * FROM (SELECT * FROM uc_chat_msg WHERE `hidden`='0' ORDER BY `id` DESC LIMIT 100) as last100 ORDER BY id"); while($row = $query->fetch_assoc()) { $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>security($row["username"]), 'message'=>security($row["message"]), 'color'=>security($row["color"])))); send_message_single_client($response_text, $socket_new);//send old messages to the new client } $query->close(); unset($changed[$found_socket]); }
Это было несколько недель назад, и я поставил проект на backburner, чтобы я мог двигаться дальше и сосредоточиться на других вещах. Поскольку, я перешел на разработку vps, чтобы удовлетворить растущий размер / сложность приложения. вместо php я использую facebook HHVM, не относящийся к делу, но думал, что хочу упомянуть об этом, так как у него есть несколько причуд, которые заставили меня немного изменить код.
Я понятия не имею о PHP, но я думаю, что это скорее проблема подхода, а не проблема с кодированием. Поэтому я надеюсь, что эти мысли из моего опыта с толстыми архитектурами принесут некоторый свет.
WebSockets использует рукопожатие HTTP, которое отправит файлы cookie для этого источника. Вы можете прикрепить временный идентификатор (например: GUID) к специальному файлу cookie сеанса, который будет повторно отправляться каждый раз, когда пользователь будет повторно подключаться в тот же сеанс. Вы можете развернуть файл cookie с веб-сайта, пока вы выполняете политику одного и того же происхождения или в ответе на подтверждение HTTP.
Когда WebSocket подключается, вы можете отправить с сервера начальное сообщение с токеном, содержащим временный идентификатор. Этот токен может повторно отправляться при повторном подключении, если соединение падает, но будет забыто, если пользователь перезагрузит страницу, если она не будет сохранена в локальном хранилище или в любом другом хранилище браузера.
Как этот временный идентификатор сопоставляется с определенным пользователем или учетной записью, представляет собой деталь реализации, которую вы НЕ ХОТИТЕ протекать в Интернете. Поэтому не используйте эту информацию для идентификации ваших WS-соединений.
Вы должны сохранить реестр «сессии», который отображает временные идентификаторы для учетных записей, которые, возможно, из словаря в памяти, в таблицу в БД. До вас, ваши требования и ваше обслуживание. Этот элемент должен поддерживать ссылку на соединение WebSocket, поэтому при необходимости вы можете получить его по временному идентификатору.
Никогда не просачивайте внутренние детали, особенно идентификаторы учетных записей (потому что они не меняются).
При получении сообщения из WebSocket вы должны создать объект сообщения, который содержит учетную запись отправителя, используя информацию о «сеансе», которую вы храните, а также фактическое содержимое сообщения. Затем передайте этот объект сообщения своей бизнес-логике, которая решит, что делать.
Чтобы отправлять сообщения, вы должны определить первые несколько элементов:
Какие учетные записи находятся в сети? Объедините хранилище «сеанс» с таблицей учетных записей. Когда соединение падает, вы должны удалить сеанс смены пароля.
Какие учетные записи принадлежат определенной группе или сигналу? Вы должны разработать это в соответствии с вашими требованиями. Некоторые группы могут быть предопределены и указаны в «учетных» сущностях, некоторые другие могут быть динамичными, как и в комнате для бесед. В любом случае вам нужен еще один элемент, который отображает «группы» в «учетные записи»,
Когда ваша бизнес-логика решает, что она хочет отправить сообщение, либо в качестве ответа на входящее сообщение, либо в качестве приуроченного действия, либо собственной инициативы, либо что-то еще … он должен решить, какую учетную запись пользователя, учетные записи пользователей или группу пользователей нужно отправить.
Затем, используя информацию о сеансе, которую вы храните, соберите список временных идентификаторов, которые должны принять это сообщение, а затем списки веб-сайтов, в которых это сообщение должно быть отправлено, и, наконец, отправить сообщение каждому из WS этого списка.