I'm currently developing a NodeJS WebSocket server. To detect broken connections I've followed this guide here:
https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
The server side works really good but the client makes problems because I can't find a ping function.
Does anyone has an idea how I can get the client part done without the library?
const WebSocket = require('ws');
function heartbeat() {
clearTimeout(this.pingTimeout);
// Use `WebSocket#terminate()`, which immediately destroys the connection,
// instead of `WebSocket#close()`, which waits for the close timer.
// Delay should be equal to the interval at which your server
// sends out pings plus a conservative assumption of the latency.
this.pingTimeout = setTimeout(() => {
this.terminate();
}, 30000 + 1000);
}
const client = new WebSocket('wss://echo.websocket.org/');
client.on('open', heartbeat);
client.on('ping', heartbeat);
client.on('close', function clear() {
clearTimeout(this.pingTimeout);
});
One main problem is that there is no ping method I think:
client.on('open') -> client.onopen available in JavaScript
client.on('close') -> client.onclose available in JavaScript
client.on('ping') -> How? Just how?
There is no Javascript API to send ping frames or receive pong frames. This is either supported by your browser, or not. There is also no API to enable, configure or detect whether the browser supports and is using ping/pong frames.
https://stackoverflow.com/a/10586583/7377682
Sad but true, in case of the ping frame, the API does not support it as mentioned in previous answers.
The most popular workaround is to listen to the close event and try to reconnect to the server using an interval.
This tutorial is easy to understand and contains most use-cases to begin with WS:
var ws = new WebSocket("ws://localhost:3000/ws");
let that = this; // cache the this
var connectInterval;
var check = () => {
const { ws } = this.state;
if (!ws || ws.readyState == WebSocket.CLOSED) this.connect(); //check if websocket instance is closed, if so call `connect` function.
};
// websocket onopen event listener
ws.onopen = () => {
console.log("connected websocket main component");
this.setState({ ws: ws });
that.timeout = 250; // reset timer to 250 on open of websocket connection
clearTimeout(connectInterval); // clear Interval on on open of websocket connection
};
// websocket onclose event listener
ws.onclose = e => {
console.log(
`Socket is closed. Reconnect will be attempted in ${Math.min(
10000 / 1000,
(that.timeout + that.timeout) / 1000
)} second.`,
e.reason
);
that.timeout = that.timeout + that.timeout; //increment retry interval
connectInterval = setTimeout(this.check, Math.min(10000, that.timeout)); //call check function after timeout
};
// websocket onerror event listener
ws.onerror = err => {
console.error(
"Socket encountered error: ",
err.message,
"Closing socket"
);
ws.close();
};
I think what you are look for on the client is onmessage:
client.onmessage = function (event) {
console.log(event.data);
}
All messages sent from the server can be listened to this way. See https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
Related
I use this code to connect to NodeJS web socket:
useEffect(() => {
let futureResponseFeedAddress = "ws://localhost:/endpoint";
const futureResponseClient = new W3CWebSocket(futureResponseFeedAddress);
props.onUpdateOrderbookWebsocket(futureResponseClient);
futureResponseClient.onopen = () => {
console.log("WebSocket Client Connected on " + futureResponseFeedAddress);
};
futureResponseClient.onmessage = (message) => {
..............
};
futureResponseClient.onclose = closeEvent => {
console.log("response messages websocket closed.");
}
return function cleanup() {
futureResponseClient.close();
};
}, []);
But after around 1-2 minutes the messages are not received. Looks like I get timeout. Do you know how I can configure the web socket to be always open?
My suggestion is you should use socket.io for doing any real-time communication . It uses http long polling ,upgrades the connection to websocket if needed and it has also good browser support . It is much realiable too. But there are some drawbacks , your server need to use socket.io server api .
Socket.io-client
Socket.io-server
There is no need to re-invent the wheel .
hell0 there!
Today I tried to send a websocket message. What am I doing wrong? I commented the code bellow so hopefully you guys can understand my goal...
// Import websocket
const websocket = require('ws');
// Create server
var socket = new websocket.Server({ port: 8080 })
// When client connects to websock server, reply with a hello world message
socket.on('connection', ws => {
ws.send('{"message":"Hello world."}'); //This works.
});
function send_message(msg){
ws.send(msg);
}
/* Calculate something, await for user interaction, etc... */
// When im done with all that, just send a message.
send_message('{"message":"please work"}'); // This does not work
What am I doing wrong?
You need to keep track of the connections. How you do that is up to you
const websocket = require('ws');
const socket = new websocket.Server({ port: 8080 })
const connections = [];
socket.on('connection', ws => {
connections.push(ws);
});
// send a new message to every connection once per second
setInterval(() => {
const date = new Date();
for (const ws of connections) {
ws.send(`hello again: ${date}`);
}
}, 1000);
Of course a real app would probably track the connections via something more complicated than just an array of connections. It would also need to stop tracking those connections when they disconnect etc...
const socket = new WebSocket('server_url'); // Connection opened
socket.addEventListener('message', function (event) { socket.send('send message'); });
I used the WebSocket like this to send the message.
I hope this will help you to fix the issues.
I'm using WebSockets to communicate between an ESP8266 (server, using the arduinoWebSockets library) and JavaScript running on a web page (client).
I'd like to notify the user when the connection between the ESP and the browser is lost.
To check if the connection still works, the client sends a "p" message every second, and the server just echoes it. If it the client doesn't receive this echo within a second, it closes the connection (WS.close();).
The WS.onclose event sets a timeout to establish a new connection.
This approach works, but the problem is that when the internet connection is lost, or when the server resets, WS.close() doesn't work as expected.
It just spends 60 seconds in the WebSocket.CLOSING state, and then times out, calling the onclose event one minute too late.
Is there a way to immediately disconnect the WebSocket connection when the server is offline?
let WS;
let WStimeout;
let pingInterval;
startWS();
function startWS() {
console.log("Start WebSocket");
WS = new WebSocket('ws://' + location.hostname + ':81/');
WS.onopen = function () {
pingInterval = setInterval(ping, 1000);
};
WS.onerror = function (err) {
console.log('WebSocket Error ', err);
};
WS.onclose = function (ev) {
console.log("WebSocket Closed ", ev);
clearInterval(pingInterval);
// ... let user know that the connection is lost
WS = null; // delete the current WebSocket
setTimeout(startWS, 5000); // try connecting to WebSocket server again in 5 seconds
}
WS.onmessage = function (ev) {
clearTimeout(WStimeout); // server is still online, clear "ping" timeout
console.log(ev.data);
// ... handle incoming data
}
}
function ping() {
if (!WS || WS.readyState !== WebSocket.OPEN) {
console.log("Connection not open: " + WS.readyState);
return;
}
WS.send("p"); // send ping to server
WStimeout = setTimeout(function () { // if it doesn't respond within a second
if (WS && WS.readyState === WebSocket.OPEN) { // and if the connection is still open
console.error("Ping timed out: closing WebSocket connection ...");
WS.close(); // close the connection
}
}, 1000);
}
Output:
Start WebSocket
p
...
p
* Turned WiFi off and on again *
Ping timed out: closing WebSocket connection ...
Connection not open: 2
... (60 times in total, for 1 minute)
Connection not open: 2
WebSocket Closed
CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: "", type: "close", …}
Start WebSocket
p
...
When I try to initialize a websocket connection to the server running on localhost with
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
in javascript, but the server hasn't completed starting up yet, I get the error
SCRIPT12029: WebSocket Error: Network Error 12029, A connection with the server could not be established
How can I prevent this? I.e. how do I check if the server has already started or how can I force the WebSocket client to wait for the server?
What about:
var webSocketFactory = {
connectionTries: 3,
connect: function(url) {
var ws = new WebSocket(url);
ws.addEventListener("error", e => {
// readyState === 3 is CLOSED
if (e.target.readyState === 3) {
this.connectionTries--;
if (this.connectionTries > 0) {
setTimeout(() => this.connect(url), 5000);
} else {
throw new Error("Maximum number of connection trials has been reached");
}
}
});
}
};
var webSocket = webSocketFactory.connect("ws://localhost:8025/myContextRoot");
When you get a connection error, you can do a limited number of trial-errors to try to re-connect. Or you can endlessly try to reach the server.
The accepted answer is perfectly fine. I just would like to extend it a little bit further with promises.
var wsFactory = { tryCount: 3,
connect : function(url){
var ctx = this,
ws = new WebSocket(url);
return new Promise(function(v,x){
ws.onerror = e => { console.log(`WS connection attempt ${4-ctx.tryCount} -> Unsuccessful`);
e.target.readyState === 3 && --ctx.tryCount;
if (ctx.tryCount > 0) setTimeout(() => v(ctx.connect(url)), 1000);
else x(new Error("3 unsuccessfull connection attempts"));
};
ws.onopen = e => { console.log(`WS connection Status: ${e.target.readyState}`);
v(ws);
};
ws.onmessage = m => console.log(m.data);
});
}
};
wsFactory.connect("ws://localhost:8025/myContextRoot")
.then(ws => ws.send("Hey..! This is my first socket message"))
.catch(console.log);
You can't prevent (or put on hold) the WebSocket from starting / establish a connection. WebSocket automatically establishes a connection with the server when its declared. What you can do is place all your code inside onopen event handler that you want to execute on successful connection. So it would be like...
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
webSocket.onopen = function() {
// code you want to execute
};
check this article to know more about WebSocket.
Hence the protocol can't get queried by the server if it is not started, the only option is trial and error.
Or you could let the WebSocket server create a simple textfile with the timestamp of the startup in your web space directory where the javascript can retrieve it and than try to establish a connection. You can retrieve the textfile with XMLHttpRequest.
I am new to node.js. How to detect client is disconnected from node.js server .
Here is my code:
var net = require('net');
var http = require('http');
var host = '192.168.1.77';
var port = 12345;//
var server = net.createServer(function (stream) {
stream.setEncoding('utf8');
stream.on('data', function (data) {
var comm = JSON.parse(data);
if (comm.action == "Join_Request" && comm.gameId =="game1") // join request getting from client
{
var reply0 = new Object();
reply0.message = "WaitRoom";
stream.write(JSON.stringify(reply0) + "\0");
}
});
stream.on('disconnect', function() {
});
stream.on('close', function () {
console.log("Close");
});
stream.on('error', function () {
console.log("Error");
});
});
server.listen(port,host);
How to know client side internet disconnection.
The best way to detect "dead sockets" is to send periodic application-level ping/keepalive messages. What that message looks like depends on the protocol you're using for communicating over the socket. Then it's just a matter of using a timer or other means of checking if you've received a "ping response" within a certain period of time after you sent the ping/keepalive message to the client.
On a semi-related note, it looks like you're using JSON messages for communication, but you're assuming a complete JSON string on every data event which is a bad assumption. Try using a delimiter (a newline is pretty common for something like this, and it makes debugging the communication more human-readable) instead.
Here is a simple example of how to achieve this:
var PING_TIMEOUT = 5000, // how long to wait for client to respond
WAIT_TIMEOUT = 5000; // duration of "silence" from client until a ping is sent
var server = net.createServer(function(stream) {
stream.setEncoding('utf8');
var buffer = '',
pingTimeout,
waitTimeout;
function send(obj) {
stream.write(JSON.stringify(obj) + '\n');
}
stream.on('data', function(data) {
// stop our timers if we've gotten any kind of data
// from the client, whether it's a ping response or
// not, we know their connection is still good.
clearTimeout(waitTimeout);
clearTimeout(pingTimeout);
buffer += data;
var idx;
// because `data` can be a chunk of any size, we could
// have multiple messages in our buffer, so we check
// for that here ...
while (~(idx = buffer.indexOf('\n'))) {
try {
var comm = JSON.parse(buffer.substring(0, idx));
// join request getting from client
if (comm.action === "Join_Request" && comm.gameId === "game1") {
send({ message: 'WaitRoom' });
}
} catch (ex) {
// some error occurred, probably from trying to parse invalid JSON
}
// update our buffer
buffer = buffer.substring(idx + 1);
}
// we wait for more data, if we don't see anything in
// WAIT_TIMEOUT milliseconds, we send a ping message
waitTimeout = setTimeout(function() {
send({ message: 'Ping' });
// we sent a ping, now we wait for a ping response
pingTimeout = setTimeout(function() {
// if we've gotten here, we are assuming the
// connection is dead because the client did not
// at least respond to our ping message
stream.destroy(); // or stream.end();
}, PING_TIMEOUT);
}, WAIT_TIMEOUT);
});
// other event handlers and logic ...
});
You could also just have one interval instead of two timers that checks a "last data received" timestamp against the current timestamp and if it exceeds some length of time and we have sent a ping message recently, then you assume the socket/connection is dead. You could also instead send more than one ping message and if after n ping messages are sent and no response is received, close the connection at that point (this is basically what OpenSSH does).
There are many ways to go about it. However you may also think about doing the same on the client side, so that you know the server didn't lose its connection either.