4

I am trying to listen to a custom event on the client with native websockets and with ws on the back end.

I first create a pool of connected clients.

import express from 'express'
import http from 'http'
import WebSocket from 'ws'
import { v4 as uuidv4 } from 'uuid'
import processes from './routes/process.js'
import { EventEmitter } from 'events'

// Express
const app = express()

// Initialize a simple http server
const server = http.createServer(app)

// Initialize the WebSocket server instance
const wss = new WebSocket.Server({ server })

let connections = {}

wss.on('connection', (socket) => {
  socket.id = uuidv4()
  connections[socket.id] = socket
  socket.send(socket.id)

  console.log(`Client connected ID: ${socket.id}`)

  socket.on('close', () => {
    delete connections[socket.id]
    delete socket.id
  })
})

And on react I store the socketID

const [socketID, setSocketID] = useState('')

useEffect(() => {
  const socket = new WebSocket('ws://localhost:4000')
  socket.onmessage = (message) => setSocketID(message.data)
}, [])

What I would like to do is then emit a message to the specific client using the socketID, I use axios from react to send the socketID in a request to an express route.

Then back at the server I use EventEmitter to pick up the client ID from the route, I have omitted the details cause is a bit lengthy, but at this point the server correctly identifies the client, the problem seems to be with sending a custom event.

connections[socketID].send('notify', 'Hello specific client')

And on the client I listen for the custom nofify event

useEffect(() => {
  const socket = new WebSocket('ws://localhost:4000')
  socket.onmessage = (message) => setSocketID(message.data)
  // Set notify listener to notify this specific client only
  socket.addEventListener('notify', (data) => {
    // I am expecting 'Hello specific client'
    console.log(data)
  })
}, [])

When I inspect react dev tools I can see that the socketID is receiving the 'custom event' and it changes the socketID to the word notify. So clearly ws is just sending the word notify and not as a custom event but as normal message that the client picks up as onmessage rather than the eventListener.

Álvaro
  • 2,255
  • 1
  • 22
  • 48

1 Answers1

3

Since I cannot find anywhere anything about ws sending custom events, I just used the only send event to do everything, I have attached an event string to the message to know which one is been triggered.

Not ideal but it works. If anyone can think of a better way, please let me know.

wss.on('connection', (socket) => {
  socket.id = uuidv4()
  connections[socket.id] = socket
  socket.send(JSON.stringify({ event: 'ID', id: socket.id }))

  console.log(`Client connected ID: ${socket.id}`)

  socket.on('close', () => {
    delete connections[socket.id]
    delete socket.id
  })
})

connections[socketID].send(
  JSON.stringify({ event: 'notify', payload: 'Hello specific client')})
)
useEffect(() => {
  const socket = new WebSocket('ws://localhost:4000')

  socket.onmessage = (message) => {

    const data = JSON.parse(message.data)

    if (data.event === 'ID') {
      setSocketID(data.id)
    }

    if (data.event === 'notify') {
      console.log(data.payload)
    }
  }

}, [])
Álvaro
  • 2,255
  • 1
  • 22
  • 48
  • This is essentially what I'm doing. I don't know how else to achieve it. Unfortunately, it seems that if more than one message is received at the same time, it doesn't work - I assign a variable to data.event and then do lots of things with it. So the variable is given its value when the first message comes through, but before the function can finish running, the second messsage re-assigns a new value for the variable. Do you know how to bind the variable to the message and stop it from changing when a new message comes through? – Normajean Jun 10 '21 at 01:56
  • Ran into this problem in Rust, needed a features where a client can't check his connection with on of our devices & send special statements to the user on the current device state. Opted for a custom data structure similar to `{event: 'notify', payload: '...'}` – CoderMan Jun 21 '23 at 21:58