How to use cluster on socket.io-client? - node.js

I want to use cluster on both side (Server and Client). I have got success on server side, but unable to do on client side. I am using node.js on both (Server side and Client side).
I am using below code on server side
var express = require('express'),
cluster = require('cluster'),
sio = require('socket.io');
var port = 3000,
num_processes = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < num_processes; i++) {
cluster.fork();
}
} else {
var app = new express();
var server = app.listen(port),
io = sio(server);
io.on('connection', function (client) {
client.on('evnt', function (data) {
console.log('evnt' + process.pid, data);
});
});
}
and on client side this one
var url = 'http://localhost:3000/';
var socket = require('socket.io-client')(url);
socket.on('connect', function () {
console.log('Connected with ', url);
setInterval(function () {
socket.emit('evnt', {sham: 'sakdf'});
}, 500)
});
socket.on('disconnect', function () {
console.log('Disconnected');
});

There is no need of cluster module on client side, simply we can create multiple process or child process if needed. We use cluster on server side because we have to bind multiple processes on same IP and port.

Related

socket.io not emitting to all clients

I'm running into a strange issue where - in my production environment only (everything works fine in local testing) - socket.emit() works fine but io.emit() does not. In other words, each individual socket client connection can send and receive messages to the server, but when the server emits to all clients, none of them receive the message. This is strange, because it can see all the clients - if I check Object.keys(io.engine.clients) I see the ids of all connected clients. But io.emit() doesn't broadcast to any of them.
I'm using the latest version of node (7.7.4 and socket.io (1.7.3). I'm hosting on Heroku, and have enabled sticky sessions. I use cluster so that all CPUs are utilized, and I have a redis service set up to synchronize all the workers. All of that infrastructure appears to be working just fine.
Why would socket.emit() work for any given socket, but none of the other methods?
socket.on('sendChat', function(messageBundle) {
console.log('sockets: ' + Object.keys(io.engine.clients)) //shows clients
io.sockets.emit('incomingChat', messageBundle); //nothing
io.emit('incomingChat', messageBundle); //also nothing
var clients = Object.keys(io.engine.clients);
for (var i = 0; i < clients.length; i++) {
console.log('broadcasting to: ' + clients[i]);
socket.broadcast.to(clients[i]).emit('incomingChat', messageBundle); //still nothing
}
socket.emit('incomingChat', messageBundle); //this works though
});
UPDATE:
Here is where I define the socket stuff earlier.
var redis = require('redis');
var pub = redis.createClient(redisPort, redisHost, {
auth_pass: redisPwd
});
var sub = redis.createClient(redisPort, redisHost, {
detect_buffers: true,
auth_pass: redisPwd
});
var adapter = require('socket.io-redis');
io.adapter(adapter({
pubClient: pub,
subClient: sub
}));
var cluster = require('cluster');
var WORKERS = process.env.WEB_CONCURRENCY || 3;
if (cluster.isMaster) {
for (var i = 0; i < WORKERS; ++i)
console.log('forking process ' + i);
cluster.fork();
} else {
var express = require('express');
var session = require('express-session');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var port = process.env.PORT || 5000;
server.listen(port, function() {
console.log("Listening on " + port);
console.log("testing");
});
var mySession = session({...}) //this enables me to authenticate the socket using the same session middleware as the express routes
io.use(function(socket, next) {
mySession(socket.handshake, {}, next);
});
io.on('connection', function(socket) {
socket.emit('welcome');
console.log('socket connection');
socket.on(... etc.)
...})
...})
On the client side, the socket connection is initiated using a simple config:
var socket = io({
reconnection: true,
reconnectionDelay: 100,
});
io is undeclared on the beginning, and then You try to redeclare it in the else statement.
For me the issue was mismatching version of socket.io and reddis-adapter (upgraded socket io to 3.0.1 and redis adapter remained at 5.x.x)
Upgrading adapted to 6.0.1 solved it

uWebSockets - push events to server

I have a uWebSockets server as it seems to be a lot more performance friendly than socket.io servers.
So I have a server and its connected fine and after some trouble I got the index.html client side to connect, but now I'm not able to push events to the server from the client side. What am I doing wrong?
var WebSocketServer = require('uws').Server,
express = require('express'),
path = require('path'),
app = express(),
server = require('http').createServer(),
createEngine = require('node-twig').createEngine;
var wss = new WebSocketServer({server: server});
wss.on('connection', function (ws) {
ws.on('join', function (value) {
console.log('SOMEONE JUST JOINED');
});
ws.on('close', function () {
//console.log('stopping client interval');
clearInterval(id);
});
});
server.on('request', app);
server.listen(8080, function () {
console.log('Listening on http://localhost:8080');
});
index.html
<script>
var host = window.document.location.host.replace(/:.*/, '');
var server = new WebSocket('ws://' + host + ':8080');
server.onmessage = function (event) {
updateStats(JSON.parse(event.data));
};
server.onopen = function (event) {
server.send("Here's some text that the server is urgently awaiting!");
server.send('join');
};
function something() {
console.log('WORKED');
server.send('join');
}
</script>
You don't have an event listener setup on the server side that does receive and react on the message. Like
ws.on('message', function (msg) {
// Do something with the message received from the client
});

How to run socketio server properly on multiple cores using cluster module of NodeJS?

Socket IO server is running fine on single instance of NodeJs. But when I'm using cluster module of NodeJS to run the servers on multiple cores I'm getting the error, "Connection closed before receiving a handshake response". I've googled the reason and found out that,
Essence of the problem is, when you run multiple Node app threads (workers) on a server, or multiple servers, socket.io clients connections are routed by cluster in a random round-robin manner, and handshaken / authorized io client requests get handed to workers where they are not handshaken / authorized, where the mess begins. Source Link
I've tried a couple of things to make it work but no success so far. Here's the code
'use strict';
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express');
var config = require('./config/environment');
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var cluster = require('cluster');
var domain = require('domain');
var socketIo = require('./config/socketio');
var REDIS = require('redis')
var store = REDIS.createClient();
var pub = REDIS.createClient();
var sub = REDIS.createClient();
if(cluster.isMaster) {
var numWorkers = require('os').cpus().length;
for(var i = 0; i < numWorkers; i++) {
cluster.fork();
}
} else {
var d = domain.create ();
d.on ("error", function (error){
// start new server
});
// Setup server
var app = express();
var server = require('http');
d.run (function (){
server = server.createServer(app);
});
require('./config/express')(app);
require('./config/redis')();
require('./routes')(app);
server.listen(config.port, config.ip, function () {
console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});
var redis = require('socket.io-redis');
var socketIO = require('socket.io')(server, {
serveClient: (config.env === 'production') ? false : true,
path: '/socket.io-client'
});
sub.subscribe('chat');
socketIO.adapter(redis( {host: 'localhost', port: 6379}));
socketIo.createSocketConnection('/dummy', socketIO, sub, pub, store);
exports = module.exports = app;
}
File: ./config/socketio
'use strict';
function addNamespaceForId (socketio, namespace, sub, pub, store){
socketio.of(namespace).on('connection', function(socket) {
onConnect(socketio, socket, namespace, sub, pub, store);
console.info('[%s] CONNECTED', socket.address);
sub.on('message', function(pattern, key){
store.hgetall(key, function(e, obj){
socket.send(obj.uid + ": " + obj.text)
})
})
socket.on('disconnect', function() {
console.info('[%s] DISCONNECTED', socket.address);
});
});
}
}
function onConnect(io, socket, namespace, sub, pub, store) {
socket.on('message', function(from, msg) {
store.incr("messageNextId", function(e, id){
store.hmset("messages:" + id, { uid: socket.sessionId, text: 'text;' }, function(e, r){
pub.publish("chat", "messages:" + id)
})
})
io.emit('broadcast', {
payload : from['message'],
source : from
});
io.of(namespace).emit('broadcast', {
payload : from['message'],
source : from
});
});
// When the client emits 'info', this listens and executes
socket.on('info', function(data) {
console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
});
// Insert sockets below
require('../api/thing/thing.socket').register(socket);
}
module.exports = {
createSocketConnection : function (namespace, socketio, sub, pub, store){
addNamespaceForId(socketio, namespace, sub, pub, store);
}
};
I've also tried using adapter for redis as suggested in the documentation.
This setup works sometimes but not always. I'm unable to figure out the missing point.

Socket.io events working only in the same process

I'm trying to use socket.io & sticky-session to pass messages to my clients.
The problem is that client which connect to one of the processes won't get messages from other processes, only from the process he is connected to.
How can I make web sockets to work across all processes?
Server.js:
var cluster = require('cluster');
var app = require('./config/express')(db);
// Init the server to run according to server CPU's
if (cluster.isMaster) {
for (var i = 0; i < 4; i++) {
cluster.fork();
}
} else {
app.listen(config.port, function () {
console.log('Process ' + process.pid + ' is listening to all incoming requests');
});
}
Process.js:
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var ns = io.of('/ns');
var sticky = require('sticky-session');
if (!sticky.listen(server, 8080)) {
// Master code
server.once('listening', function() {
console.log('server started on 8080 port');
});
}
client.js:
var io = require('socket.io-client');
var serverUrl = 'http://localhost:8080/ns';
var conn = io.connect(serverUrl);
conn.on('malware', function(infectedProcess){
console.log('infectedProcess: ' + infectedProcess);
});

Simple node.js socket server

I am trying to create a simple node.js server that will allow my socket based iOS app to send it's GPS coordinates to the server, and the server will broadcast the GPS coordinate to all connected iOS clients. Similarly, the clients are connected to the server using sockets. I tried using some sample code from Heroku's web server. CODE IS EDITED TO INCLUDE ANURAG'S ANSWER
var WebSocketServer = require("ws").Server
var http = require("http")
var express = require("express")
var app = express()
var port = process.env.PORT || 5000
app.use(express.static(__dirname + "/"))
var server = http.createServer(app)
server.listen(port)
console.log("http server listening on %d", port)
var wss = new WebSocketServer({server: server})
console.log("websocket server created")
var connectionList = [];
wss.on("connection", function(ws) {
console.log("connection");
connectionList.push(ws);
})
wss.on("message", function(data, id) {
var mes = server.unmaskMessage(data);
var str = server.convertToString(mes.message);
console.log(str);
var i;
for(i = 0; i < connectionList.lenth; i++) {
wss.sendMessage(one, str, connectionList[i]);
}
});
How do I modify this code to be able to receive messages from my app (via sockets) and then send that message to all other iOS clients connected. (The message is just a simple string)
BONUS QUESTION: Because Heroku makes you use it's environments port (rather than your own specified one), in my iOS app, when I connect to the server, would I just specify the Port that is printed to the console when the server is started.
Any help is appreciated, Thank you! :)
EDIT: For broadcasting to the clients, the code is:
wss.on('connection', function(ws) {
ws.on('message', function(message) {
wss.broadcast(message);
});
});
However how do I receive messages from a client, and how do I make the received message the message to be broadcasted to the other clients.
On getting the connection you need to store those connections.
Then you can send message to all those devices connect to your server using those connections.
You may try something like this:
var connectionList = [];
wss.on("connection", function(ws) {
connectionList.push(ws);
})
wss.on("message", function(data, id) {
var mes = server.unmaskMessage(data);
var str = server.convertToString(mes.message);
console.log(str);
var i;
for(i = 0; i < connectionList.lenth; i++) {
wss.sendMessage(one, str, connectionList[i]);
}
});
Read more here: https://www.npmjs.com/package/websocketserver
Here is the complete index.js code sothat the server brodcast received messages to clients:
var WebSocketServer = require("ws").Server
var http = require("http")
var express = require("express")
var app = express()
var port = process.env.PORT || 5000
app.use(express.static(__dirname + "/"))
var server = http.createServer(app)
server.listen(port)
console.log("http server listening on %d", port)
var wss = new WebSocketServer({server: server})
console.log("websocket server created")
wss.broadcast = function(data) {
for (var i in this.clients)
this.clients[i].send(data);
};
wss.on("connection", function(ws) {
console.log("websocket connection open");
ws.on('message', function(message) {
console.log("message received by server");
wss.broadcast(message);
})
ws.on("close", function() {
console.log("websocket connection close")
})
})

Resources