1

I am currently creating a websocket server for a mobile front. and in some cases I need to send a json only to a specific user and after several attempts I still haven't managed to get Websocket to work so I can send my json to one client at a time.

i'm using this library : github.com/websockets/ws

To explain my problem i have several products that contain several variables that need to be refreshed in real time. when a user connects to a product he will receive only the json of that product and the other users will receive the json of the other products they are currently on. that's why i want to send a specific json to a user to enable this

I would like to know if any of you know how to fix the problem as I'm starting to block on it.

Thank you very much.

const opp = new WebSocket.Server({port: 10001});
let user = 0;
let lookup = [];

opp.on('connection', function connection(op) {
    lookup.push(op.id);
    let id = "";
    op.on('message', function incoming(message) {
        console.log('received: %s', message);
        id = message;
        query = {
            text: "SELECT id,state,users_list_name,user_win,timer_stamp FROM products WHERE id = " + parseInt(id) + " AND circle = 1 ORDER BY CASE WHEN state = \'available\' THEN \'1\' WHEN state = \'soon\' THEN \'2\' WHEN state = \'expired\' THEN \'3\' END",
        };
    });
    client.connect();
    const interval = setInterval(function ping() {
        client.query(query, (err, res) => {
            if (err) {
                console.log(err.toString());
                console.log(query);
            } else {
                console.log(lookup);
               for (let i = 0; i < lookup.length; i++){
                    console.log("########################");
                    lookup[i].send(JSON.stringify(res.rows));
                }
            }
        });
    }, 300);
});``` 
11
  • 1
    Why does your title say socket.io, but your code uses a plain webSocket? Which is it? Commented Jan 23, 2020 at 0:46
  • It was a mistake, thank you for pointing that out. Commented Jan 23, 2020 at 0:50
  • So, lookup[i] is going to be an id from op.id. Is that really what you can send to? Which websocket server module are you using so I can look at its API? Also, the first sentence of your question still mentions socket.io. I see you have corrected the title. Commented Jan 23, 2020 at 0:50
  • If you want to send the data to only the client that started this interval, then instead of looping through all the lookup array, just do op.send(). That will represent the client that sent you the message that set the query that corresponds with this interval. Commented Jan 23, 2020 at 0:54
  • FYI, running a separate setInterval() on a 300ms frequency for every single connected webSocket is not going to scale at all. You're database is going to fast become a bottleneck. I don't know what problem you're trying to solve here, but you will need a different/better architecture that doesn't have every single client separately hitting your database every 300ms. Commented Jan 23, 2020 at 0:56

1 Answer 1

2

OK. Still trying to understand the actual spec you're shooting for. But, assuming the following (based on your answers to my prior questions):

  1. A client connects using a webSocket.
  2. When they send a message over that webSocket, that message is an id of something that can be looked up in your database and that they want regular updates for.
  3. Those updates for that particular id should be sent only to that specific client that requested it.
  4. If a different client connects and specifies some id, they should get updates for that id only.
  5. When a client sends a new message that specifies a different id, their updates should now be only for that new id.
  6. Updates for the id that one client requested are sent only to that one client (not to the other clients).

If that's what you really want, here's a way to structure that.

const wss = new WebSocket.Server({port: 10001});

// make database connection that all users share
client.connect();

wss.on('connection', function connection(ws) {
    // these variables are unique to each ws connection
    let interval, query;

    // when webSocket closes, stop any current interval timer associated with this webSocket
    ws.on('close', function() {
        if (interval) {
            clearInterval(interval);
        }
    });

    // when we get an id, start querying for updates on that id
    ws.on('message', function incoming(id) {
        console.log(`received: ${id}`);
        query = {
            text: "SELECT id,state,users_list_name,user_win,timer_stamp FROM products WHERE id = " + parseInt(id) + " AND circle = 1 ORDER BY CASE WHEN state = \'available\' THEN \'1\' WHEN state = \'soon\' THEN \'2\' WHEN state = \'expired\' THEN \'3\' END",
        };
        // if interval is not already going, start it
        if (!interval) {
            interval = setInterval(function() {
                client.query(query, (err, res) => {
                    if (err) {
                        console.log(err);
                        console.log(query);
                    } else {
                        // send data to just the one client that this timer is for
                        ws.send(JSON.stringify(res.rows));
                    }
                });
            }, 300);
        }
    });
});

Now, some comments:

  1. Polling the database on a short time interval with a separate polling loop for every single client simply will not scale at all. You will have serious database scale issues. You really need a better design here, but since we don't know the overall requirements and architecture of your application, we don't have enough info to know what to suggest. Probably you want to leverage notifications in a database that tell you when data has changed rather than you polling it on a short interval on behalf of every single client.

  2. I could find no reason for the lookup data structure. Your comments say that you want to send updates to ONE specific client, the one that requested that id. That can be done with ws.send().

  3. This code assumes that the client variable represents a connection to your database that each of the setIntervals for each connected client can all share. That's why that code was moved out of the wss.on('connection', ...) event handler.

  4. I switched to the more common terminology of wss to refer to the server instance and ws to refer to the webSocket for a particular connected client.

  5. ws.send() is how you send to a connected client. I still don't know what you were doing with op.id. Looking at the doc for the ws library, that doesn't appear to be something you can use to send to.

  6. Your code (and this code) creates a separate setInterval() timer for every webSocket client that connects and it uses a very short interval time. This will not scale. At worst, the interval time needs to be lengthened into multiple seconds (depending upon desired target scale). At best, you need to stop polling the database entirely and use some other mechanism in the database for getting notifications when data has been changed.

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

1 Comment

that exactly what i'm trying to do, thanks for the time you spended on it . i'm new on nodejs so maybe i wasn't the most accurate in my responses, sorry for that. thanks

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.