1

My application stack:

On my server runs a Redis server. The PHP backend communicates with Predis library with the Redis server. It will publish messages. These messages will be fetched by my Redis client (node.js) and pushed to the connected websocket clients (with SockJS).

My problem:

It runs well. At least for broadcast messages. Now I came to the point I need to send a unicast message and I'm stuck... How to connect the user on the backend side (sender of messages) with the connected client of the websocket?

Code snippets:

PHP

$redis = new Client();
$redis->publish('updates', Random::getUniqueString());

Redis client on node.js server

redis.subscribe('updates');

redis.on('message', function(channel, data) {
    for (var id in sockets) {
        if (sockets.hasOwnProperty(id)) {
            sockets[id].write(data);
        }
    }
});

SockJS client

mySocketFactory.setHandler('message', function(event) {
    console.log(event.data);
});

Like I said. Working well but the id used for the socket connection is not known by the PHP backend.

Edit: One idea I got in mind is to use cookies.

0

1 Answer 1

0

I found a way to solve my problem. When the socket connection is established I sent a request to my PHP backend and ask for the user id. This is stored on the node.js server. When messages are incoming there is a check if they are for specific user and handle them only for them.

So, what do I store exactly on my node server?

var sockets = {}; // {connection_id: socket_connection}

var connIdToUser = {}; // {connection_id: user_id}

var connIdsForUser = {}; // {user_id: [connection_id_1, connection_id_2 ,...]}


socketServer.on('connection', function(conn) {
    sockets[conn.id] = conn;

    var options = {
        host: os.hostname(),
        port: 80,
        path: '/user/id',
        method: 'GET'
    };
    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            var userId = JSON.parse(chunk).id;
            connIdToUser[conn.id] = userId;

            if (!connIdsForUser.hasOwnProperty(userId)) {
                connIdsForUser[userId] = [];
            }
            connIdsForUser[userId].push(conn.id);

            console.log('connection id ' + conn.id + ' related to user id ' + userId);
        });
    });
    req.end();

    conn.on('close', function() {
        console.log('connection lost ' + conn.id);

        // remove connection id from stack for user
        var connections = connIdsForUser[connIdToUser[conn.id]];
        var index = connections.indexOf(conn.id);
        if (index > -1) {
            connections.splice(index, 1);
        }

        // remove connection at all
        delete sockets[conn.id];
        // remove relation between connection id and user
        delete connIdToUser[conn.id];
    });
});

The reason for storing the relation between user id an connection id twice is the different use case I need either for sending a message or deleting the connection for the close event. Otherwise I would have to use a nested loop. As you can see deleting a socket is fairly easy. Although deleting the connection from the connection stack of an user is a little bit complicated.

Let's continue with the sending of a message. Here I defined a structure of the message I get from the Redis server:

{
  targets: [], // array of unit ids (can be empty)
  data: <mixed> // the real data
}

Sending the data to the sockets looks like:

redis.on('message', function(channel, message) {
    message = JSON.parse(message);

    // unicast/multicast
    if (message.targets.length > 0) {
        message.targets.forEach(function(userId) {
            if (connIdsForUser[userId] !== undefined) {
                connIdsForUser[userId].forEach(function(connId) {
                    sockets[connId].write(message.data);
                });
            }
        });
    // broadcast
    } else {
        for (var id in sockets) {
            if (sockets.hasOwnProperty(id)) {
                sockets[id].write(message.data);
            }
        }
    }
});

Since I store the connection stack per user it is quite easy to send the data to all sockets related to a specific user. So what I can do now is unicast (array with one user id), multicast (array with more than one user id) and broadcast (empty array).

It's working well for my use case.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.