How to send messages to particular users Ratchet PHP Websocket

28.5k views Asked by At

I'm trying to build a system where user can subscribe to a category on establishing connection to websocket server and then he will start receiving updates for that category. So far, I have worked with Ratchet and I am able to send message to all the connected clients but the problem is I don't want to send message to all the clients I just want to send the message to the clients who have subscribed the the particular category in which the messages was sent.

PHP Code

Chat.php

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

class Chat implements MessageComponentInterface
{
    protected $clients;

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

    public function onOpen(ConnectionInterface $conn)
    {
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $conn, $msg)
    {
        foreach ($this->clients as $client)
        {
            if($conn !== $client)
                $client->send($msg);
        }
    }

    public function onClose(ConnectionInterface $conn)
    {
        $this->clients->detach($conn);
    }

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

server.php

<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

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

$server = IoServer::factory(
  new HttpServer(
    new WsServer(
      new Chat()
    )
  ),
  8080
);

$server->run();
?>

Client side js code

<script type="text/javascript">
var conn = new WebSocket('ws://localhost:8080');

conn.onopen = function(e) {
  console.log("Connection established!");
};

conn.onmessage = function(e) {
  console.log(e.data);
};
</script>
3

There are 3 answers

5
MarshallOfSound On BEST ANSWER

Basically you want a syntax for sending data to the WebSocket, I reccomend using a JSON object to do this. In your WebSocket class you need a local variable called subscriptions and a local variable called users like so.

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

class Chat implements MessageComponentInterface
{
    protected $clients;
    private $subscriptions;
    private $users;

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

    public function onOpen(ConnectionInterface $conn)
    {
        $this->clients->attach($conn);
        $this->users[$conn->resourceId] = $conn;
    }

    public function onMessage(ConnectionInterface $conn, $msg)
    {
        $data = json_decode($msg);
        switch ($data->command) {
            case "subscribe":
                $this->subscriptions[$conn->resourceId] = $data->channel;
                break;
            case "message":
                if (isset($this->subscriptions[$conn->resourceId])) {
                    $target = $this->subscriptions[$conn->resourceId];
                    foreach ($this->subscriptions as $id=>$channel) {
                        if ($channel == $target && $id != $conn->resourceId) {
                            $this->users[$id]->send($data->message);
                        }
                    }
                }
        }
    }

    public function onClose(ConnectionInterface $conn)
    {
        $this->clients->detach($conn);
        unset($this->users[$conn->resourceId]);
        unset($this->subscriptions[$conn->resourceId]);
    }

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

The javascript to go with that looks a bit like this

<script type="text/javascript">
var conn = new WebSocket('ws://localhost:8080');

conn.onopen = function(e) {
  console.log("Connection established!");
};

conn.onmessage = function(e) {
  console.log(e.data);
};

function subscribe(channel) {
    conn.send(JSON.stringify({command: "subscribe", channel: channel}));
}

function sendMessage(msg) {
    conn.send(JSON.stringify({command: "message", message: msg}));
}
</script>

Note: This code is untested I wrote it on the fly from my experience with Ratchet. Good luck :)

0
Anurag K On

ok, now I am sharing my experience. You Can Send Token and Insert This toke in onOpen (server.php) in the database. You can Insert Chat Token When User Login. Every Time When User Login New Token is generated and updated. Now Use This Token To Find out user Id in onOpen connection.

var conn = new WebSocket('ws://172.16.23.26:8080?token=<?php echo !empty($_SESSION['user']['token']) ? $_SESSION['user']['token'] : ''; ?>');    
    conn.onopen = function(e) {
        console.log("Connection established!");
    };

Now Server.php

  public function onOpen(ConnectionInterface $conn) {
    $this->clients->attach($conn);      
    $querystring = $conn->httpRequest->getUri()->getQuery();
    parse_str($querystring,$queryarray);
   //Get UserID By Chat Token
    $objUser = new \users;
    $objUser->setLoginToken($queryarray['token']);
    $GetUserInfo = $objUser->getUserByToken();
    echo "New connection! ({$conn->resourceId})\n";
    //save Chat Connection
   // Insert the new connection with the user ID to Match to later if user ID in Table Then Update IT with a new connection 

 }

Now your Connection is established, then send Message To particular users

Client-Side send USerID with received ID and Message

$("#conversation").on('click', '#ButtionID', function () {
         var userId      = $("#userId").val();
         var msg         = $("#msg").val();         
         var receiverId  = $("#receiverId").val();
      if ( userId  != null &&  msg  != null ){
        var data = {
            userId: userId,
            msg: msg,
            receiverId: receiverId
        };
        conn.send(JSON.stringify(data));
        $("#msg").val("");  
      }                 
    });

Server Side

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);
      echo $data['userId'];
      echo $data['msg'];
      echo $data['receiverId'];
     // Save User ID receiver Id message in table 
     //Now Get Chat Connection by user id By Table Which is saved onOpen function    
        $UptGetChatConnection   = $objChatroom->getChatConnectionUserID($data['receiverId']);
    $ReciId = $UptGetChatConnection[0]['ConnectionID'];
    foreach ($this->clients as $client) {
        if ($from == $client) {
           $data['from']  = "Me";
           $data['fromname'] = 5;
        } else {
           $data['from']  = $user['name'];
           $data['fromname'] = 6;
        }
        echo $client->resourceId;
        echo $ReciId."\n";
        if($client->resourceId == $ReciId || $from == $client){
            $client->send(json_encode($data));
        }
    }
} 

now the message has been sending by connection ID not broadcast only send particular users

1
Rodrigo Santana On
// SOCKET VARIABLE DECLARATION
var mySocket;

// MESSAGE
const socketMessageListener = function(event) {
    var data = JSON.parse(event.data);
};

// OPEN SOCKET
const socketOpenListener = function(event) {
    console.log('Connected');
};

// CLOSE OR DISCONNECT SOCKET
const socketCloseListener = function(event) {
    if (mySocket) {
        console.error('Disconnected.');
    }
    mySocket = new WebSocket('ws://localhost:8080');
    mySocket.addEventListener('open', socketOpenListener);
    mySocket.addEventListener('message', socketMessageListener);
    mySocket.addEventListener('close', socketCloseListener);
};

// CALL THE LISTENER
socketCloseListener();

function sendMessage(data) {
    mySocket.send(JSON.stringify(data));
}

user3049006 i found this code in my research to keep the connection persistent if fail retry the connection again.