ws: websocket server unable to handle requests immediately after server startup.
-
I’ve searched for any related issues and avoided creating a duplicate issue.
-
I’ve checked to make sure this is the module with the issue.
Description
Server completes handshake with client websocket before being able to handle messages from client immediately after server startup.
Reproducible in:
version: 5.2.0
Node.js version(s):8.9.4
OS version(s): OSX 10.12.6 (MacOS High Sierra)
Steps to reproduce:
- mount
express-wsonserver - have client continually try to connect to websocket server while server is starting.
Expected result:
Messages sent by client after handshake are handled by the server immediately after server startup.
Actual result:
Messages sent by client after handshake are never handled by the server (websocket is opened appropriately). The function connectionHandler defined in the app.ws(path, connectionHandler) is never called with the messages.
If client delays the sending of these messages by a small amount of time (around 50 milliseconds on my computer) the server handles the messages appropriately.
Attachments:
Server
server sample code
//import ws
const enableWs = require('express-ws')
//lots of extraneous code
//attach ws to server
enableWs(app, server)
app.ws('/ws', webSocketConnectionHandler) //see below
//more extraneous code
//tell the server to listen on port
server.listen(port, ()=> {
console.log(`server listening on port ${port}`)
});
webSocketConnectionHandler
function wsConnectionHandler(ws, req) {
//prevent websocket closing from crashing the server
ws.on('close', () => {})
//prevent websocket error from crashing the server
ws.on('error', (error) => {})
ws.on('message', message => {
console.log(message) //verifying the message was received
webSocketMessageHandler(message, getDefaultMessageHandlers(ws, req), ws) //handle message
})
}
Client (not working, but should) - Java
//simplified a lot
class webSocket extends WebSocketClient {
public webSocket(String url) {
super(new URI(url), new Draft_6455(), getHeaders(), 0);
}
private boolean alreadyConnecting = false;
//runs once websocket successfully opens (handshake successful)
public void onOpen(ServerHandshake serverHandshake) {
this.send("Hello World"); //this message is not handled by the server but should
Timer timer = new Timer(true);
timer.schedule(new TimerTask() { // delays sending of message over websocket for 50 milliseconds
@Override
public void run() {
this.send("Hola Teirra"); //this message is handled by the server
timer.cancel();
}
}, 50);
}
//once websocket closes or fails to initially connect, keep trying to reconnect until successful
public void onClose() {
if (alreadyConnecting) {
return;
}
this.alreadyConnecting = true;
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() { //try to reconnect every second
@Override
public void run() {
try {
if (this.reconnectBlocking()) {
this.alreadyConnecting = false;
timer.cancel();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 1000);
}
}
What happens:
The "Hello world" message is sent by the client after handshake completes and websocket successfully opens, however the "Hello world" message is never handled by the server. The "Hola Teirra" message is sent 50 milliseconds after the websocket successfully opens and the message is successfully handled by the server. I have verified that the client successfully sends both messages.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 16 (8 by maintainers)
Does this mean something like this?
If so then yes, you may miss messages if you don’t add a listener for the
'message'event immediately after (on the same tick) receiving the'connection'event.@Workruft the way the nodejs event loop works ensures that this won’t happen unless you do something asynchronous before making your
ws.on(...calls.So as long as you aren’t making those calls in a callback or after an
awaitstatement, you will be fine 😃The connection is established asynchronously, which means it won’t be established until all synchronous code that’s already been queued up is completed. The
ws.oncalls are synchronous, so your example is fine, although I can completely understand why you might be confused.So basically,
.on()calls are only setting up state, nothing actually initiates until the script ends. Makes sense, gotcha, thanks for the explanation.I have a highly related question to this one, so I’ll ask it here. In your examples for WS, I see this kind of code:
However, this really bothers me, even if it’s sort of trivial: Couldn’t a connection technically be established before any of the event handler functions are registered?
I’m not really sure how the async nature of Node/JS works here, but it does seem that you can immediately chain .on() calls after creating the new WebSocket. Why aren’t the code examples doing this, and why not just have a .startListening() call instead?