nodejs websocket detect disconnected socket - node.js

I have a nodejs websocket server and I have the following problem.
When my clients connect to the server and terminate gracefully the onclose method is called on those sockets and I perform clean up operations on the closed socket.
When the client disconnects due to network, the onclose method is not called. Is there any timeout to be set so onclose is called automatically after a timeout?
I am using ws package for the websocket server in nodejs

default ws implementation doesn't have a callback for network disconnects from client side
You can find a keepAlive implementation here

Well I'll try to answer your question with two examples. Try to analyze both of them and learn how they work. They are both tested and working.
1- Websocket:
Server:
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(3000, function() {
console.log((new Date()) + ' Server is listening on port 3000');
});
wsServer = new WebSocketServer({
httpServer: server,
autoAcceptConnections: false
});
function originIsAllowed(origin) {
return true;
}
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});
Client:
<!DOCTYPE html>
<html>
<head>
<title>Web socket Experiment</title>
<script type="text/javascript">
function callWebSocket() {
var socket = new WebSocket("ws://localhost:3000", 'echo-protocol');
socket.onopen = function () {
alert("Hello, Connected To WS server");
};
socket.onmessage = function (e) {
alert("The message received is : " + e.data);
};
socket.onerror = function (e) {
alert("An error occured while connecting... " + e.data);
};
socket.onclose = function () {
alert("hello.. The coonection has been clsoed");
};
}
</script>
</head>
<body>
<input type="button" value="Open Connecton" onclick="callWebSocket()" />
</body>
</html>
2- Socket.io:
Server:
var http = require('http');
var app = require('express')();
var httpServer = http.createServer(app)
var io = require('socket.io')(httpServer);
app.get('/', function(req, res) {
res.sendfile(__dirname + '/index.html');
});
io.on('connection', function(socket) {
socket.emit('news', {
hello: 'world'
});
socket.on('my other event', function(data) {
console.log(data);
});
socket.on('disconnect', function(data) {
console.log('disconnect!');
});
});
httpServer.listen(3000);
Client:
<html>
<head>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
var conn_options = {
'sync disconnect on unload':false
};
var socket = io.connect('http://localhost:3000',conn_options);
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
</head>
<body>
</body>
</html>

To detect a disconnect, you need to have some traffic. If your application produces constant traffic, then you could do something like reset a counter each time something is received, and consider the connection failed when the counter runs out.
Otherwise you should be able to use the pings that WebSocket offers. These are not exposed in the browser, but your WebSocket library for Node.js may allow you to turn them on and get a notification if a ping fails.

You can check it in the official library documentation. I don't want to copy-paste it here because it can be out of date soon.

Related

Websocket load balancing using nginx within a node js client and server command line application

I am creating a app where I can run three different nodejs servers and have a client connect to one of the three. However, in case there are is more load on one server than the other, I want to use nginx server to proxy all requests to a different node server on a different port.
Below is my client.js
#!/usr/bin/env node
var WebSocketClient = require('websocket').client;
var client = new WebSocketClient();
client.on('connectFailed', function(error) {
console.log('Connect Error: ' + error.toString());
});
client.on('connect', function(connection) {
console.log('WebSocket Client Connected');
connection.on('error', function(error) {
console.log("Connection Error: " + error.toString());
});
connection.on('close', function() {
console.log('echo-protocol Connection Closed');
});
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log("Received: '" + message.utf8Data + "'");
}
});
});
client.connect('ws://localhost:8080/', 'echo-protocol');
server.js
#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8080, function() {
console.log((new Date()) + `Server is listening`);
});
wsServer = new WebSocketServer({
httpServer: server,
autoAcceptConnections: false
});
function originIsAllowed(origin) {
return true;
}
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});
I know nginx is primiarly used for webpages but I was wondering if it could be used for something like this when creating an application where you are connecting to a server and there is too much load, you are proxied to a new port. If so, would something like this work?
upstream nodes {
ip_hash;
server ws://localhost:8080/;
server ws://localhost:8081;
server ws://localhost:8082;
}
server {
listen 3000;
location / {
proxy_pass http://nodes;
}
}
Any help would be appreciated or sense of direction.
Thank you

Socket.io makes connection on https://domain but doesnt make any on https://domain/route

Okay as I said i the title when I go to my home page the socket connection works perfectly but when I use a route it doesnt work at all here is my index.js
io.of('/admin').on('connection', function(socket) {
console.log('made socket connection', socket.id);
console.log(socket.request.user);
socket.on('chat', async function(data) {
console.log(data);
client.guilds.channels.get('474951005788962846').send(data);
io.sockets.emit('chat', data);
});
});
io.on('connection', function(socket) {
console.log('made socket connection', socket.id);
console.log(socket.request.user);
socket.on('chat', async function(data) {
console.log(data);
client.guilds.channels.get('474951005788962846').send(data);
io.sockets.emit('chat', data);
});
});
and on my /admin page here is my script that it is the same but the connection isnt as I added the /admin
<script>
var socket = io.connect('https://domain/admin');
// Query DOM
var serverID = document.getElementById('add_server_id');
var serverRoles = document.getElementById('add_role_ids');
var btnServer = document.getElementById('add_server_save');
//var output = document.getElementById('output');
// Emit events
btnServer.addEventListener('click', function(){
socket.emit('chat', {
serverid: serverID.value,
serverroles: serverRoles.value
});
});
//Listen for events
socket.on('chat', function(data) {
console.log(data);
//output.innerHTML += '<p><strong>' + data.game + '</strong></p>';
});
</script>
if someone can tell me why is it not connecting I would really appreciate it
var socket = io.connect('https://domain/admin');
to
var socket = io.connect('/admin');
The namespace is an implementation detail of the Socket.IO protocol,
and is not related to the actual URL of the underlying transport,
which defaults to /socket.io/….

How to clear redis database when nodejs server disconnect?

I have a simple chatroom application using a node express server.
This uses a redis database connection to store the nicknames of the joined clients.
I need to clear the redis SET of nicknames named members when the server is closed/disconnected.
This can be done as following:
redisClient.del("members", function(err, reply){
console.log("members set delete :" + reply);
});
But where should I put this code? How to handle the final event from the server when disconnection, from the server side?
Server code - chatroom.js
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('redis');
var redisClient = redis.createClient();
io.on('connection', function(client){
console.log("client connected...");
});
io.on('connection', function(client){
client.on('join', function(name){
client.nickname = name;
//adding names
client.broadcast.emit("add member", name);
redisClient.smembers('members', function(err, names) {
names.forEach(function(name){
client.emit('add member', name);
});
});
client.emit('add member', client.nickname)
redisClient.sadd("members", name);
});
// remove clients on disconnect
client.on('disconnect', function(name){
client.broadcast.emit("remove member", client.nickname);
redisClient.srem("members", client.nickname);
});
});
app.get('/', function(req, res){
res.sendFile(__dirname + '/views/index.html');
});
server.listen(8080);
Client code - views/index.html
<html>
<head>
<title>Socket.io Client</title>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h2>Chat box</h2><br>
<h4 id="status"></h4><br>
<div>
<h3>Active members</h3>
<ul id="members"></ul>
</div>
<script>
var socket = io.connect('http://localhost:8080');
socket.on('connect', function(data){
nickname = prompt("What is your nickname?");
$('#status').html('Connected to Chat Room as \''+nickname+'\'.');
socket.emit('join', nickname);
});
socket.on('add member', function(name) {
var member = $('<li>'+name+'</li>').data('name', name);
$('#members').append(member);
});
socket.on('remove member', function(name) {
$('#members li').filter(function() { return $.text([this]) === name; }).remove();
});
socket.on('disconnect', function(data){
$('#status').html('Chatroom Server Down!');
});
</script>
</body>
</html>
How to clear the redis database set when nodejs server disconnect?
you can use error or end events on redisclient, check the Redis Package Documentation
redisClient.on("error", function (err) {
console.log("Error " + err)
// delete here
});
However, since your connection is closed, it is more healthy to delete on first connection to redis each time. do it on reconnection state too.
When a socket.io connection dies, an event named disconnect is fired. Register your reset logic to that callback.
io.sockets.on('connection', function (socket) {
socket.on('disconnect', function () {
redisClient.del("members", function(err, reply){
console.log("members set delete :" + reply);
});
});
});
Credits : How can i handle Close event in Socket.io?

Websocket connection in Node.js closes on sending

I tried many different npm web socket libraries (WebSocket, ws, express-ws and more), and in EVERYONE of them I have the same problem. When I try to send a websocket message, the connection closes.
I have no problem receiving messeges, only sending.
Here is one simple example of one test with express and ws libraries:
Node.JS side:
var server = require('http').createServer()
, url = require('url')
, WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ server: server })
, express = require('express')
, app = express()
, port = 8080
, fs = require('fs');
app.use(function (req, res) {
var index = fs.readFileSync('./interface/index.html');
res.end(index);
});
wss.on('connection', function connection(ws) {
var location = url.parse(ws.upgradeReq.url, true);
console.log('open ws');
ws.on('message', function incoming(message) {
console.log('received: %s', message);
ws.send('test back');
});
ws.on('close', function () {
console.log('ws connection closed.');
})
});
server.on('request', app);
server.listen(port, function () { console.log('Listening on ' + server.address().port) });
and the browser side ("./interface/index.html"):
window.WebSocket = window.WebSocket || window.MozWebSocket;
var port = 8080;
var ip = window.location.hostname;
var connection;
connection = new WebSocket('ws://' + ip + ':' + port);
connection.onopen = function () {
// connection is opened and ready to use
console.log('Web socket connection with', ip + ':' + port);
//sendQueue(msgQueue);
};
connection.onerror = function (error) {
// an error occurred when sending/receiving data
console.log('Web socket erorr with', ip + ':' + port + ':', error);
};
connection.onmessage = function (message) {
// try to decode json (I assume that each message from server is json)
console.log("Received ws");
// handle incoming message
};
connection.onclose = function (){
console.log("Web socket connection lost.");
};
function sendws() {
connection.send("test");
console.log("sent ws");
return false;
}
<head>
<meta charset="utf-8">
<title>Simple Web Socket test</title>
<!--<meta name="description" content="Simple Web Socket">-->
<meta name="author" content="Binoman">
<link rel="stylesheet" href="css/styles.css?v=1.0">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<button type="button" name="button" onclick="sendws()">Send</button>
</body>
I have no idea why this is happening. I updated Node.js to 6.7.
Running on windows 10.
I appreciate the help!
Thanks!
I discovered it was my antivirus's web protection that blocked the connection. I made an exception for my localhost address and it working perfectly now.

Create WebSockets between a TCP server and HTTP server in node.js

I have created a TCP server using Node.js which listens to clients connections.
I need to transmit data from TCP server to HTTP server again in Node.js possibly through a Websocket (socket.io).
However, I do not know how to create such connection such that TCP server is able to push data to HTTP server through Websocket.
Many Thanks.
I was trying lot of things to get this work. Most of the time I was relying on socket.io to get this working, but it was just not working with TCP.
However, net.Socket suffices the purpose.
Here is the working example of it.
TCP Server
var net = require('net');
var HOST = 'localhost';
var PORT = 4040;
var server = net.createServer();
server.listen(PORT, HOST);
server.on('connection', function(sock) {
console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
sock.write("TCP sending message : 1");
console.log('Server listening on ' + server.address().address +':'+
server.address().port);
}).listen(PORT, HOST);
HTTP Server
var http = require('http').createServer(httpHandler),
fs = require("fs"),
wsock = require('socket.io').listen(http),
tcpsock = require('net');
var http_port = 8888;
var tcp_HOST = 'localhost';
var tcp_PORT = 4040;
/**
* http server
*/
function httpHandler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
http.listen(http_port);
console.info("HTTP server listening on " + http_port);
wsock.sockets.on('connection', function (socket) {
var tcpClient = new tcpsock.Socket();
tcpClient.setEncoding("ascii");
tcpClient.setKeepAlive(true);
tcpClient.connect(tcp_PORT, tcp_HOST, function() {
console.info('CONNECTED TO : ' + tcp_HOST + ':' + tcp_PORT);
tcpClient.on('data', function(data) {
console.log('DATA: ' + data);
socket.emit("httpServer", data);
});
tcpClient.on('end', function(data) {
console.log('END DATA : ' + data);
});
});
socket.on('tcp-manager', function(message) {
console.log('"tcp" : ' + message);
return;
});
socket.emit("httpServer", "Initial Data");
});
Browser Client
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('httpServer', function (data) {
console.log(data);
document.write(data + "\r\n");
socket.emit('tcp', "For TCP");
});
</script>
This way, there is a socket opened between HTTP server and TCP server in Node.js.
If you need to communicate server-server than websockets is probably not a best choice. Try one of RPC libraries, or just use HTTP or your own protocol.
You can use either socket.io or ws (only WebSocket) on Node.js as client (not only in browser)
var io = require('socket.io-client');
var socket = io.connect('http://IP address of Websocket server');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});

Resources