workerman icon indicating copy to clipboard operation
workerman copied to clipboard

How to send a message to the client from the backend server ?

Open Muradg opened this issue 2 years ago • 3 comments

Hey! First of all, I want to thank you for your library. I have a problem that I don't know how to solve yet.

I have a chat in which I implemented sending simple messages, everything works fine. Now I want to send files and ran into a problem with large file sizes. I decided not to load the connection socket and send files directly to the backend server via form/data.

BUT. How, after a successful send, initialize the user and send him a socket notification that his file was successfully sent? At the moment my code looks like this:

` $ws_worker = new Worker('websocket://'.config('websockets.server'));

// Emitted when new connection come
$ws_worker->onConnect = function (TcpConnection $connection) {

    $connection->onWebSocketConnect = function($connection) {

        if (isset($_GET['token']) && $token = PersonalAccessToken::findToken($_GET['token'])) {
            $user = $token->tokenable;

            $this->clients[$connection->id] = [
                'user' => $user,
                'connection' => $connection,
            ];
        }
        return;
    };
};


$ws_worker->onMessage = function (TcpConnection $connection, $message) {

    if (!array_key_exists($connection->id, $this->clients)) {
        echo "Error. User not authorized\n";
        return;
    }

    $sender = $this->clients[$connection->id];

    $data = json_decode($message, true);

    // send message to DB

    $connectionClientsUniques = ArrayHelper::arrayColumnsWithSaveKeys($this->clients, 'client_unique');
    $findedConnectionsClients = array_keys($connectionClientsUniques, $roomMember['client_unique'], true);

    foreach ($findedConnectionsClients as $findedConnectionClient) {
        // SEND MESSAGE FOR CONNECTION
    }
};


$ws_worker->onClose = function($connection)
{
    echo "Close connection ID ".$connection->id."\n";

    if (array_key_exists($connection->id, $this->clients)) {
        unset($this->clients[$connection->id]);
        echo "FINDED CONNECTION ID  ".$connection->id." AND LOST CONNECTIONS - ".count($this->clients)." \n";
    }
};

// Run worker
Worker::runAll();

`

My problem is that I don't know where to store active user sessions. So that I can access them from other parts of the project. Now they are stored in my clients variable in the same place where the websocket server starts. Should they be stored elsewhere? Where do you recommend? And also, please tell me how to send notifications to the desired connection from any part of the project. For example, I imagined it like this:

` send_alert.php

<?php
use Workerman\Worker;

$ws_worker = new Worker('websocket://11.11.111.111');
$ws_worker->findByConnection(...)->send($data);

`

Muradg avatar Jan 06 '23 17:01 Muradg

Create an http_worker in ws_worker's onWorkerStart, http_worker and ws_worker will be in the same process space,so http_worker can access any variables of ws_worker. When an external program wants to send ws data, it only needs curl call http_worker. The code looks like this.

$ws_worker = new Worker('websocket://'.config('websockets.server'));

$ws_worker->onWorkerStart = function($ws_worker)
{
    $http_worker = new Worker('http://0.0.0.0:2023');
    $http_worker->onMessage = function($http_connection, $request) use ($ws_worker){
        $to_user = $request->post('to_user');
        $msg = $request->post('msg');
        foreach ($this->clients as $client) {
            if ($client['user'] == $to_user) {
                $client['connection']->send($msg);
                break;
            }
        }
    };
    $http_worker->listen();
};

$ws_worker->onConnect = ...

Curl codes.

$to_user = "xxx";
$url = "http://127.0.0.1:2023/";
$post_data = array(
   "msg" => "hello",
   "to_user" => $to_user, 
);
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $push_api_url );
curl_setopt ( $ch, CURLOPT_POST, 1 );
curl_setopt ( $ch, CURLOPT_HEADER, 0 );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
curl_setopt ($ch, CURLOPT_HTTPHEADER, array("Expect:"));
$return = curl_exec ( $ch );
curl_close ( $ch );
var_export($return);

walkor avatar Jan 08 '23 10:01 walkor

Hello! Trying to replicate the code, but I need to send data from the internal php-server to all connected users.

Server codes:

$ws_worker = new Worker('websocket://0.0.0.0:2346');
$ws_worker->count = 1;

$ws_worker->onWorkerStart = function($ws_worker) {
    $http_worker = new Worker('http://127.0.0.1:2023');
    $http_worker->onMessage = function($http_connection, $request) use ($ws_worker) {
        // this code does not work:
        foreach($ws_worker->connections as $connection) {
            $connection->send($request);
        }
    };
    $http_worker->listen();
};

// this code works correctly:
$ws_worker->onMessage = function ($con, $data) use ($ws_worker) {
    foreach($ws_worker->connections as $connection) {
        $connection->send($data);
    }
};

my CURL codes:

$url = "http://127.0.0.1:2023";
$data = "{"json": "data"}";
$ch = curl_init ();
curl_setopt_array($ch, [
 CURLOPT_URL => $url,
 CURLOPT_POSTFIELDS => $data,
 CURLOPT_POST => true
]);
$curl_result = curl_exec($ch);
curl_close($ch);
var_export($curl_result); // it's true

what am i doing wrong? help please.

khristyan avatar Feb 14 '23 11:02 khristyan

Look at my example in https://github.com/iandreyev/websocket-salesman/blob/master/index.php for send message from backend script Example send message in https://github.com/iandreyev/websocket-salesman/blob/master/tests/message.php

vladandreevg avatar Jun 21 '24 19:06 vladandreevg