websockets / ws

Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js
MIT License
21.33k stars 2.3k forks source link

Dual Triggering of WebSocket Events - ws.on('message') and stream.on('data') #2195

Closed shubhams7134 closed 5 months ago

shubhams7134 commented 5 months ago

Is there an existing issue for this?

Description

Issue details When interacting with a WebSocket connection on the client side, we observed that both the ws.on('message') and stream.on('data') events on the server side are triggered concurrently. This behavior occurs whether data is sent via ws.send() or through a WebSocket stream created using createWebSocketStream. We seek clarification on whether this is an expected behavior and, if so, guidance on how to differentiate between data coming from a stream and discrete messages on the server side.

UseCase If dual triggering is expected, guidance on how to differentiate on the server side between data originating from a stream and discrete WebSocket messages. This information is crucial for handling different use cases, such as storing streamed data as bufferMessage and initiating processing upon stream completion.

Server Side code

const WebSocket = require('ws')
const port = 9000
const wss = new WebSocket.Server({ port })

wss.on('connection', ws => {
  const stream = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' })
  stream.on('data', (chunk) => {
    console.log('Server stream received data chunk ') 
  })

  stream.on('end', () => {
    console.log('Stream ended')
  })

  ws.on('message', (msg)=>{
    console.log('onMessage function called')
  })
})

console.log(`WebSocket server started on port ${port}`)
lpinca commented 5 months ago

This is expected. Every 'message' event pushes the message in the Duplex stream.

If dual triggering is expected, guidance on how to differentiate on the server side between data originating from a stream and discrete WebSocket messages.

What do you mean? It's the same data. The difference is that a chunk of data from the 'data' event might not correspond to a full WebSocket message if the stream is paused or piped to a slower destination.

shubhams7134 commented 5 months ago

What do you mean? It's the same data. The difference is that a chunk of data from the 'data' event might not correspond to a full WebSocket message if the stream is paused or piped to a slower destination.

ClientSide Code

const client = new WebSocket('ws://localhost:9000')
const stream = createWebSocketStream(client, { encoding: 'utf8' })
const fsStream = fs.createReadStream(filePath, 'utf8')
fsStream.pipe(stream)
client.send(JSON.stringify(testInput))

In the above program, since the WebSocket-stream is piped with the fsStream, whenever we get data packets being sent to the server, both ws.on('message') and stream.on('data') get triggered. And same is the case with client.send().

So on the server side, how can we differentiate a data packet of stream and a full message?

lpinca commented 5 months ago

I still don't understand what you want to achieve/differentiate. The Duplex stream is a wrapper for the WebSocket so the data you get from the Duplex stream is the same data received by the WebSocket. There is no distinction.

shubhams7134 commented 5 months ago

I'll try to explain our use case and the difficulty we are facing.

Objective: We want the ability to stream data (large blob objects) from our client to server using websockets. For that, we are making use of "WebSocket.createWebSocketStream" and we are trying to pipe the client's stream to this websocket stream. The aim is to transmit this data as a stream to the server. On the server side, we're expecting another websocket stream to receive the message.

Issue: While implementing this, both ws.on('message') and stream.on('data') are getting triggered which is causing issues because ws.on('message') receives a (incomplete) message chunk which is causing issues.

Our assumption/understanding stream.on('data') --> This should be triggered whenever we are receiving a data chunk from a client ws stream. ws.on('message') --> This should be triggered for normal ws.send messages from client

Query: We want to understand if there's a way to skip hitting ws.on('message') for streams. If ws.on('message') will get anyway get triggered, we want to understand if there's a way to distinguish between normal WebSocket (non-stream) messages and WebSocket stream messages.

lpinca commented 5 months ago

stream.on('data') --> This should be triggered whenever we are receiving a data chunk from a client ws stream.

That is what happens.

ws.on('message') --> This should be triggered for normal ws.send messages from client

That is also what happens.

We want to understand if there's a way to skip hitting ws.on('message') for streams.

No.

If ws.on('message') will get anyway get triggered, we want to understand if there's a way to distinguish between normal WebSocket (non-stream) messages and WebSocket stream messages.

There is no distinction. It works like this:

The WebSocket protocol is message based. A WebSocket message is a chunk of data in the stream.

lpinca commented 5 months ago

When you do fsStream.pipe(stream), every chunk of data read from the file is sent via ws.send(). The other peer receives each chunk in a separate 'message' event and the data of every message event is added to the stream readable queue via readable.push().

lpinca commented 5 months ago

I'm closing this as answered.