Как отправить сообщение конкретному пользователю Ratchet?
Я разрабатываю чат платформу. В качестве серверной части использую Laravel + библиотеку Ratchet для работы с сокетами.
Когда пользователь октрывает чат, устанавливается соединение по сокетам. Однако, когда собеседник пишет сообщение в чат, то оно отправляется всем остальным, а не конкретному второму собеседнику в чате. (ВАЖНО: чат только 1 на 1)
Я перечитал много туториалов и посмотрел большое количество примеров, однако я все также не понимаю, как мне отправить сообщение конкретному пользователю? Я сохранию id подключения в отдельном массиве users, но как оттуда выбрать нужного мне пользователя - не могу понять.
Стоит отметить, что в базу данных сообщения приходят правильно, они закреплены за нужными чат комнатами. Вот мод класс чата:
class ChatSocket extends BaseSocket
{
protected $clients;
protected $users;
public function __construct()
{
$this->clients = new \SplObjectStorage();
$this->users = [];
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
$this->users[$conn->resourceId] = $conn;
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg)
{
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
$data = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);
// Save to Database
$message = new MessageController();
$responseData = $message->store($data);
foreach ($this->clients as $client) {
if ($client === $from) {
$client->send(json_encode($responseData, JSON_THROW_ON_ERROR));
}
}
}
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();
}
Вот код клиента (очень топорный, в качестве id я отправляю айдишники пользователей (берется из БД).
form.addEventListener('submit', function (e) {
e.preventDefault();
let data = {
sender_id: 1,
to: 2,
chat_room_id: 1,
message: this.message.value
};
socket.send(JSON.stringify(data));
})
socket.onmessage = function(event) {
let data = JSON.parse(event.data);
console.log(data);
}
Я понимаю, что мне нужно что-то вроде такого, однако как получить этот самый $to - не понимаю.
public function onMessage(ConnectionInterface $to, $msg) {
foreach ($this->clients as $client) {
if ($to== $client) {
$client->send();
}
}
}
Ответы (1 шт):
Решил проблему с использованием БД. Вот код, может кому пригодится
class ChatSocket extends BaseSocket
{
protected $clients;
protected $message;
protected $audioClients;
public function __construct()
{
$this->clients = new \SplObjectStorage();
$this->message = new MessageController();
$this->audioClients = [];
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg)
{
$responseData = [];
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv === 1 ? '' : 's');
$data = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);
if ($data['type'] === 'subscribe') {
$this->updateSocketId($data['sender_id'], $from->resourceId);
} elseif ($data['type'] === 'message') {
$receiverIds = $this->getSocketIdByChatRoom($data['sender_id'], $data['receiverId']);
$message = $this->message->store($data);
$responseData = [
"sender_id" => $message->sender_id,
"message" => $message->message,
"audio" => $message->audio,
"chat_room_id" => $message->chat_room_id,
"created_at" => date('H:i', strtotime($message->created_at))
];
foreach ($this->clients as $client) {
foreach ($receiverIds as $receiver) {
if ($client->resourceId === $receiver->socket_id) {
$client->send(json_encode($responseData, JSON_THROW_ON_ERROR));
}
}
}
} elseif ($data['type'] === 'call') {
$receiverId = DB::table('users')->select('socket_id')->
where('id', [$data['receiverId']])->value('socket_id');
$responseData = [
'type' => $data['type'],
'sender_id' => $data['sender_id'],
'receiverId' => $data['receiverId'],
'voice_audio' => $data['voice_audio']
];
foreach ($this->clients as $client) {
if ($client->resourceId === $receiverId) {
dump('Sending...');
$client->send(json_encode($responseData, JSON_THROW_ON_ERROR));
}
}
}
}
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();
}
public function updateSocketId($userId,$socketId): int
{
return DB::table('users')->where('id', $userId)->update(['socket_id' => $socketId]);
}
public function getSocketIdByChatRoom($senderId, $receiverId): \Illuminate\Support\Collection
{
return Db::table('users')->select('socket_id')->whereIn('id', [$senderId, $receiverId])->get();
}
}