0

Hello my StackOverflow friends, I am emitting from a socket situated inside an express.js controller function as I need to notify the user once his profile has been completed.

I need to emit to the specific client who called the API endpoint and triggered the controller function.

Any idea on how to best do that?

I was thinking of sending the socket_id from the client to the server inside the API call?

Thanks!

EDIT I realised the issue lies in the fact that express.js and socket.io do not share session data, they're not wired up in the middle-ware. This post devises a solution. socket.io and express 4 sessions

Community
  • 1
  • 1
Karl
  • 179
  • 15
  • The key here is that you have to be able to identify which client it is the is sending the API call and be able to figure out server-side which socket.io connection is theirs. Usually, you do that via a session object and when they connect with socket.io, you put their socket object into the session. So, then when you get an API call, you can reach into the session object to get the socket or socket.id and can then do `socket.emit()`. – jfriend00 Dec 14 '16 at 01:02
  • Are you using a session? If so, which one? – jfriend00 Dec 14 '16 at 01:03
  • Yes that's the approach I wanted to follow, to put the socket id inside the session and send it back to the server together with the API call. Yes I do use a session, but what do you mean by 'which one'? The problem is that I don't seems to be able to see what the socket id is on the client side, only on the server side. – Karl Dec 14 '16 at 11:08
  • I meant which module are you using to give you a session? express-session? socket.id is only available on the server unless you explicitly send it to the client in a message or put it in a cookie. But, you haven't explained why you even need it on the client. – jfriend00 Dec 14 '16 at 15:52
  • Ok thanks. Yes I use express-session. Sending the socket id from client to server was an approach I imagined, but I realised it doesn't make sense, also socket id is not readily available on the client side. Everything has to happen on the server side. I realised that the issue is due to socket.io and express.js not sharing the session. This post devises a solution: http://stackoverflow.com/questions/23494016/socket-io-and-express-4-sessions. Alternatively I managed to authenticate each socket with the session token. I can then associate a socket id to a token (instead of a session id). – Karl Dec 14 '16 at 20:55
  • I am just not sure whether working with token & socket id is equivalent to working with session id and socket id. – Karl Dec 14 '16 at 20:56
  • @Karl Can you post the code as to how you solved this issue? i am at the same point. My problem however is - socket is emitting to all clients and race condition is happening. – Uma Maheshwaraa Jun 04 '17 at 04:41
  • @UmaMaheshwaraa you need to use this package https://www.npmjs.com/package/socket.io.users – Karl Jun 05 '17 at 06:18
  • @Karl will look into this. Can this package isolate tabs? in the npm readme it mentions one user for all open tabs. Just wanted to clarify – Uma Maheshwaraa Jun 06 '17 at 03:54
  • @UmaMaheshwaraa good question, I haven't tried myself (I use three different browsers for testing different users logged in at the same time), so I would rely on what the readme says. – Karl Jun 07 '17 at 05:08

2 Answers2

1

Below implementation solves the problem of communicating the socket that initiated the API request. It follows the idea of sending the socketid through the request. Sharing session variables did not work when the user opens multiple tabs in the same browser as the browser assigns same cookie/session across tabs.

Server code

  socketio.on('connection', function(socket) {
    socket.address = `${socket.request.connection.remoteAddress}:${socket.request.connection.remotePort}`;

    //  USED TO SET THE SOCKET ID FOR THE REQUEST
    socket.emit('initialize', socket.id)


    socket.connectedAt = new Date();
}

Client Code
In Angular, i setup an initialize event to store the specific socket Id for the tab

    this.socket.on('initialize', socketid => {
        console.log(socketid)
        this.socketid = socketid
    })


When the request is sent to express, i send the socketid as a header

return this.$http({
    method: 'POST',
    withCredentials: true,
    host: '127.0.0.1',
    data: sim_data,
    port: 3000,
    responseType: "arraybuffer",
    headers: {
        "X-socketid": this.socketid
    },
    url: '/api/blah'
})

Server Code
Finally, when express processes the POST or get i emit to the socketid from Header

    // GET SOCKET ID FROM HEADER
    var socket_id = req.headers['x-socketid']

    if (typeof socket_id !== 'undefined' && socket_id) {
        var io = req.app.get('io')
        io.sockets.connected[socket_id].emit('pyr', output)
    }

My next step is to integrate this with user authorization.

Uma Maheshwaraa
  • 563
  • 2
  • 9
  • 17
  • I haven't actually tested your code but I can see the following problem: when server emits `socket.emit('initialize', socket.id)`, it emits it to all clients, so all active clients will capure that event and store it into their `this.id`. What needs to be done is, as suggested in this article [http://www.codershood.info/2016/01/24/sending-message-specific-user-socket-io/]: when socket connects to client, client side should emit its userId (or username) back to server. When the even is captured on the server-side, then the server can associate userId with socketId. – Karl Jun 18 '17 at 08:31
  • @Karl Actually socket.emit in the above implementation will not emit to all clients because the socketio.on.. event is fired only when "a" socket connects and the socket within the event refers to that individual socket. socketio.emit would emit to all active clients. – Uma Maheshwaraa Jun 19 '17 at 04:40
-1

use socket.emit inside of your socket handler like this:

var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

io.on('connection', function(socket){
  socket.on('someEndpoint', function(message){
    socket.emit('someClientEndPoint', 'You sent a message to someEndpoint');
  });
});
Jim Factor
  • 1,465
  • 1
  • 15
  • 24
  • 2
    This misses that they want to use `socket.emit()`, but they want to use it from an incoming API call. – jfriend00 Dec 14 '16 at 01:03