user13755987
user13755987

Reputation:

How to detect which message was sent from the Websocket server

I have a small web application listening for incoming messages from a Websocket server. I receive them like so

const webSocket = new WebSocket("wss://echo.websocket.org");
webSocket.onopen = event => webSocket.send("test");
webSocket.onmessage = event => console.log(event.data);

but the sending server is more complex. There are multiple types of messages that could come e.g. "UserConnected", "TaskDeleted", "ChannelMoved"

How to detect which type of message was sent? For now I modified the code to

const webSocket = new WebSocket("wss://echo.websocket.org");

webSocket.onopen = event => {
  const objectToSend = JSON.stringify({
    message: "test-message",
    data: "test"
  });

  webSocket.send(objectToSend);
};

webSocket.onmessage = event => {
  const objectToRead = JSON.parse(event.data);

  if (objectToRead.message === "test-message") {
    console.log(objectToRead.data);
  }
};

So do I have to send an object from the server containing the "method name" / "message type" e.g. "TaskDeleted" to identify the correct method to execute at the client? That would result in a big switch case statement, no?

Are there any better ways?

Upvotes: 2

Views: 5994

Answers (2)

synthet1c
synthet1c

Reputation: 6282

As you have requested in one of your comments to have a fluent interface for the websockets like socket.io.

You can make it fluent by using a simple PubSub (Publish Subscribe) design pattern so you can subscribe to specific message types. Node offers the EventEmitter class so you can inherit the on and emit events, however, in this example is a quick mockup using a similar API.

In a production environment I would suggest using the native EventEmitter in a node.js environment, and a browser compatible npm package in the front end.

Check the comments for a description of each piece.

The subscribers are saved in a simple object with a Set of callbacks, you can add unsubscribe if you need it.

note: if you are using node.js you can just extend EventEmitter

// This uses a similar API to node's EventEmitter, you could get it from a node or a number of browser compatible npm packages.
class EventEmitter {
  // { [event: string]: Set<(data: any) => void> }
  __subscribers = {}
  
  // subscribe to specific message types
  on(type, cb) {
    if (!this.__subscribers[type]) {
      this.__subscribers[type] = new Set
    }
    this.__subscribers[type].add(cb)
  }
  
  // emit a subscribed callback
  emit(type, data) {
    if (typeof this.__subscribers[type] !== 'undefined') {
      const callbacks = [...this.__subscribers[type]]
      callbacks.forEach(cb => cb(data))
    }
  }
}

class SocketYO extends EventEmitter {
  
  constructor({ host }) {
    super()
    // initialize the socket
    this.webSocket = new WebSocket(host);
    this.webSocket.onopen = () => {
      this.connected = true
      this.emit('connect', this)
    }
    this.webSocket.onerror = console.error.bind(console, 'SockyError')
    this.webSocket.onmessage = this.__onmessage
  }
  
  // send a json message to the socket
  send(type, data) {
    this.webSocket.send(JSON.stringify({
      type,
      data
    }))
  }
  
  on(type, cb) {
    // if the socket is already connected immediately call the callback
    if (type === 'connect' && this.connected) {
      return cb(this)
    }
    // proxy EventEmitters `on` method
    return super.on(type, cb)
  }
  
  // catch any message from the socket and call the appropriate callback
  __onmessage = e => {
    const { type, data } = JSON.parse(e.data)
    this.emit(type, data)
  }
}

// create your SocketYO instance
const socket = new SocketYO({
  host: 'wss://echo.websocket.org'
})

socket.on('connect', (socket) => {
  // you can only send messages once the socket has been connected
  socket.send('myEvent', {
    message: 'hello'
  })
})

// you can subscribe without the socket being connected
socket.on('myEvent', (data) => {
  console.log('myEvent', data)
})

Upvotes: 0

Eriks Klotins
Eriks Klotins

Reputation: 4180

You can avoid the big switch-case statement by mapping the methods directly:

// List of white-listed methods to avoid any funny business
let allowedMethods = ["test", "taskDeleted"];

function methodHandlers(){
  this.test = function(data)
  {
     console.log('test was called', data);
  }

  this.taskDeleted = function(data)
  {
     console.log('taskDeleted was called', data);
  }
}


webSocket.onmessage = event => {
  const objectToRead = JSON.parse(event.data);
  let methodName = objectToRead.message;
  if (allowerMethods.indexOf(methodName)>=0)
  {
     let handler = new methodHandlers();
     handler[methodName](data);
  }
  else
  {
     console.error("Method not allowed: ", methodName)
  }
 };

Upvotes: 2

Related Questions