ratchetphp / Ratchet

Asynchronous WebSocket server
http://socketo.me
MIT License
6.23k stars 722 forks source link

Get List Of Connected Users From Ratchet WebSocket #1022

Open varun7952 opened 1 year ago

varun7952 commented 1 year ago

I am trying my hands on websockets and i think its working too. I created a dummy php file to start a websocket server then at onOpen i am attaching to user ID of each clients so i can access it later or can use their ID to send message to them. After starting and connecting to the websocket, i try to access list of users connected to the websocket like this

<?php 
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once('websocket.php');

// Test Connected Device/Users
$connectedClients = $webSocketServer->getClients();
foreach ($connectedClients as $userId => $client) {
    echo "User ID: $userId, Connection: $client\n";
}

?>

This file shows error that port is already in use, its right because websocket server is running on 20081 port and all users connected to websocket on this port too.

<br />
<b>Fatal error</b>:  Uncaught RuntimeException: Failed to listen on &quot;tcp: //0.0.0.0:20081&quot;: Address already in use (EADDRINUSE) in /home/bitnami/vendor/react/socket/src/TcpServer.php:184
Stack trace:
#0 /home/bitnami/vendor/react/socket/src/Server.php(77): React\Socket\TcpServer-&gt;__construct()
#1 /home/bitnami/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php(59): React\Socket\Server-&gt;__construct()
#2 /opt/bitnami/apache/htdocs/net/websocket.php(171): Ratchet\Server\IoServer: :factory()
#3 /opt/bitnami/apache/htdocs/net/test.php(6): require_once('/opt/bitnami/ap...')
#4 {main
}
  thrown in <b>/home/bitnami/vendor/react/socket/src/TcpServer.php</b> on line <b>184</b><br />

Websocket

<?php

require_once("/home/bitnami/vendor/autoload.php");
use Ratchet\MessageComponentInterface;;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

class WebSocketHandler implements MessageComponentInterface {
    private $clients;
    public function __construct() {
        $this->clients = new \SplObjectStorage();
    }

    public function onOpen(ConnectionInterface $conn) {
        // Handle new WebSocket connection

        $request = $conn->httpRequest;
        // Get the action
        $action = $request->getHeaderLine("action");
        // Check if the action is "addClient"
        if ($action === "addClient") {
            // Add the user to the list of connected users
            $userId = $request->getHeaderLine('userId');            
            // Save the user ID in the resourceId property of the connection
            $conn->resourceId = $userId;
            // Add the connection to the clients list
            $this->clients->attach($conn, $userId);
        }
        file_put_contents('zee1.log',print_r($userId.' '.$action,true));        
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }

    private function findClientConnection($userId) {
        foreach ($this->clients as $client) {
            if ($client->resourceId === $userId) {
                return $client;
            }
        }
        return null;
    }

    public function getClients(){
        $connectedClients = [];
        foreach ($this->clients as $client) {
            $connectedClients[$client->resourceId] = $client;
        }
        return $connectedClients;
    }   

}

$webSocketServer = new WebSocketHandler();

    $server = IoServer::factory(
            new HttpServer(
                new WsServer(
                    $webSocketServer
                )
            ),
            20081
        );

    $server->run();

I stuck to get connected users from websocket, but not able to find any docs or help which can help me to get active/connected users (with their IDs).

SimonFrings commented 1 year ago

@varun7952 Thanks for bringing this up :+1:

I can see in your example above that you use SplObjectStorage in order to store your clients, attach them inside the onOpen() method and detach them in onClose(). So it seems to me that you're already keeping track of your connected clients, what exactly is the problem you're facing here (What makes you think it doesn't work)?

varun7952 commented 1 year ago

@SimonFrings Is there any method or function which i can call to get ID of users currently connected to websocket. Right now i am doing it by saving user id at onOpen in text file and delete at onClose but i guess this is the hack not a solution. Can you please look at this point ?

programarivm commented 10 months ago

@varun7952 you may also want to store the connected clients in a PHP array.

    // ...

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

        // ...
    }

    // ...

Then, the ids can be obtained using PHP's built-in function array_keys.

    array_keys($this->clients);