Giorgio Robino
Giorgio Robino

Reputation: 2265

socket io client-server/"unicast" channels: how to implement them?

I want to realize a client/server architecture (web browser clients <-> backend nodejs server), using a communication protocol to exchange 1-to-1 binary/voice messages between web clients and the backend server (a "voice assistant/bot").

I chose socketio as websocket protocol. This is the basic message exchange flow:

  1. clients requests to the server (pull mode)

    • Each js client, running on a web browser, send an audio/voice message to the nodejs server.
    • The server does some elaboration on the inbound message and reply the sender client with an outbound voice message ONLY to the specific client.
  2. server notification to a client (push mode)

    • under some server-side events, the server send an unsolicited message a certain client
                           +----------------+   +------------------+
                           |                |   |                  |
         +--+              |                |   |                  |
         |  | web browser  |                |   |                  |
user 1   |  +--------------> https          |   |                  |
         |  <--------------+ web server     |   |                  |
         +--+              | for            |   |                  |
                           | static assets  |   |                  |
         +--+              |                |   |                  |
         |  | web browser  |                |   |      cobot       <--------+
user 2   |  +-------------->                +--->      dialog      +------+ |
         |  <--------------+                +---+      manager     |      | |
         +--+              | socketio       |   |      server      |      | |
                           | server         |   |      logic       |      | |
                           | for audio /    |   |                  |      | |
         +--+              | video          |   |                  |      | |
         |  | web browser  | messages       |   |                  |      | |
 user N  |  +-------------->                |   |                  |      | |
         |  <--------------+                |   |                  |      | |
         +--+              |                |   |                  |      | |
                           |                |   |                  |      | |
                           +----------------+   +------------------+      | |
                                                                          | |

Maybe one could call "unicast" this client-server communication. Server side, that's the usual behavior of a web server that receives a request from a certain client and serve the request replying to this client.

I'm confused about out to use socketio API to realize the scenario described. If I well understood socketio architecture:

    // sending to sender-client only
    socket.emit('messageType', 'message payload');

Is room socketio concept a solution? I think about assigning a room to each client (a user), allowing users to connect from different devices with a user_id (room_id = user_id). In this scenario, I guess each client have to join his "dedicated" room. Right?

UPDATE

reading interesting question /answers: socket.io private message and How synchronise socketIO connection ID's on client and server?, I sketched this pseudocode, following the idea to associate a room to each private bidirectional client/server channel:

The client:

//
// client side
//

const user_id = 'username'
const audioBlob = ... // audioChunks blob from MediaRecorder


// join the room with his user_id as name
socket.join(user_id)

// new user (= room) registration
// notify the server about this new connection
socket.emit('user_id', user_id)

...

// client send a message (request) to the server 
// on room with name user_id, excluding sender as recipients
socket.broadcast.to(user_id).emit('audioMessage', audioBlob)

...

// client receive a message  
// audio message received from server (the answer to the request)
socket.on('audioMessage', audioBlob => 
  playAudio(audioBlob) )

The server:

//
// server side
//
io.on('connection', socket => {

   // pair/associate the socket with a room name
   socket.on('user_id', user_id => {
     socket.join(user_id)
     // store somewhere association: {socket.id, user_id}
     storeOnDB(socket, user_id) 
   })

   // server receives a message from a client (pull request)
   // the server elaborates the message  
   // and sends back to the user an answer 
   // (all clients in 'uid' room except sender, the server itself)
   socket.on('audioMessage', msg => {

     // retrieve the room name  to which socket belongs
     const  user_id = getFromDB(socket)

     socket.broadcast.to(user_id).emit('audioMessage', answerTo(msg))
   })
}

...

// server sends an unsolicited/push message to a user
// (client socket on a room) 
io.to('some uid').emit('some notification', data)

Does the pseudo-code make sense/it's correct?

Upvotes: 0

Views: 980

Answers (1)

Giorgio Robino
Giorgio Robino

Reputation: 2265

I found a simple running solution that avoid any user_id <-> room concept.

  • That's working with just socket.emit() for the client/server request/replies. In a way, socket.id act as session_id.

  • Almost satisfying, but to manage unsolicited/push messages from server to client. I need to store/retrieve session_id (socket.id):

  // just use socket id
  // no room / no user id

  //
  // client side
  //
  const audioBlob = // some audioChunks blob from MediaRecorder

  // ..
  // client send a message (request) to the server
  socket.emit('audioMessage', audioBlob)

  // ...
  // client receive a message
  // audio message received from server (answering the request)
  socket.on('audioMessage', audioBlob =>
    playAudio(audioBlob) )


  //
  // server side
  //
  io.on('connection', socket => {

     // store connected socket (optional, only for unsolicited push)

     // server receives a message from a client (pull request)
     // the server elaborates the message
     // and sends back to the user an answer
     socket.on('audioMessage', msg =>
       socket.emit('audioMessage', answerTo(msg)) )

  })


  // ...
  // retrieve connected socket
  // server sends an unsolicited/push message to a user
  someSocket.emit('some notification', data)

This solution seems to work, even if the room=uid based solution proposed in my question maybe is better because allows to have multiple devices connected at the same time (honestly not a business logic requirement in my case).

Upvotes: 1

Related Questions