Socket.IO messaging to multiple rooms - node.js

I'm using Socket.IO in my Node Express app, and using the methods described in this excellent post to relate my socket connections and sessions. In a comment the author describes a way to send messages to a particular user (session) like this:
sio.on('connection', function (socket) {
// do all the session stuff
socket.join(socket.handshake.sessionID);
// socket.io will leave the room upon disconnect
});
app.get('/', function (req, res) {
sio.sockets.in(req.sessionID).send('Man, good to see you back!');
});
Seems like a good idea. However, in my app I will often by sending messages to multiple users at once. I'm wondering about the best way to do this in Socket.IO - essentially I need to send messages to multiple rooms with the best performance possible. Any suggestions?

Two options: use socket.io channels or socket.io namespaces. Both are documented on the socket.io website, but in short:
Using channels:
// all on the server
// on connect or message received
socket.join("channel-name");
socket.broadcast.to("channel-name").emit("message to all other users in channel");
// OR independently
io.sockets.in("channel-name").emit("message to all users in channel");
Using namespaces:
// on the client connect to namespace
io.connect("/chat/channel-name")
// on the server receive connections to namespace as normal
// broadcast to namespace
io.of("/chat/channel-name").emit("message to all users in namespace")
Because socket.io is smart enough to not actually open a second socket for additional namespaces, both methods should be comparable in efficiency.

Related

Is there a better solution than socket.io for slow-speed in-game chat?

I am creating a browser game with node.js (backend api) and angular (frontend). My goal is to implement an in-game chat to allow communication between players on the same map. The chat is not an essential part of the game, so messages don't need to be instant (few seconds of latency should be ok). It is just a cool feature to talk some times together.
A good solution should be to implement socket.io to have real-time communication. But as chat is not an essential component and is the only thing which will require websockets, i'm wondering if there is not an alternative to avoid server overload with sockets handling.
I thinked about polling every 2 or 3 seconds my REST API to ask for new messages, but it may overload server the same way... What are your recommandations?
Thank you for your advices
There's a pretty cool package called signalhub. It has a nodejs server component and stuff you can use in your users' browsers. It uses a not-so-well-known application of the http (https) protocol called EventSource. EventSource basically opens persistent http (https) connections to a web server.
It's a reliable and lightweight setup. (The README talks about WebRTC signalling, but it's useful for much more than that.)
On the server side, a simple but effective server setup might look like this:
module.exports = function makeHubServer (port) {
const signalhubServer = require('signalhub/server')
const hub = signalhubServer({ maxBroadcasts: 0 })
hub.on('subscribe', function (channel) {
/* you can, but don't have to, keep track of subscriptions here. */
})
hub.on('publish', function (channel, message) {
/* you can, but don't have to, keep track of messages here. */
})
hub.listen(port, null, function () {
const addr = hub.address()
})
return hub
}
In a browser you can do this sort of thing. It user GET to open a persistent EventSource to receive messages. And, when it's time to send a message, it POSTs it.
And, Chromium's devtools Network tab knows all about EventSource connections.
const hub = signalhub('appname', [hubUrl])
...
/* to receive */
hub.subscribe('a-channel-name')
.on('data', message => {
/* Here's a payload */
console.log (message)
})
...
/* to send */
hub.broadcast('a-channel-name', message)

I can't get my head around websockets (via socket.io and node.js)

I'm new to websockets/socket.io/node.js. I'm trying to write a card game app, but pretty much all the example tutorials I've found are creating chat applications. So I'm struggling to get my head around the concepts and how they can be applied to my card game.
Keeping it simple, the card game will involve two players. The game involves moving cards around the table. Each player has to see the other player's moves as they happen (hence the need for constant connections). But the opponents cards are concealed to the other.
So two people browse to the same table then click to sit (and play, when both seats are taken). Using
io.on("connection", function(sock){
//socket events in here
});
am I creating the one socket ('io', or 'sock'?) that both clients and the server share, or is that two separate sockets (server/clientA and sever/clientB)? I ask, because I'm struggling to understand what's happening when a message is emitted and/or broadcast. If a client emits a message, is that message sent to both the server and the other client, or just the server? And then, further does it also send the message to itself as well?? It seems as though that's the logic... or what is the purpose of the 'broadcast' method?
From a functional perspective, I need the server to send different messages to each player. So it's not like a chatroom where the server sends the chat to everyone. But if it's one socket that the three of us share (clients and server), how do I manage who sees what? I've read about namespaces, but I'm struggling to work out how that could be used. And if it's two separate sockets, then I can more easily imagine sending different data to the separate clients. But how is that implemented - is that two 'io' objects, or two 'sock' objects?
Finally, I've got no idea if this is the sort of long-winded question that is accepted here, so if it's not, can someone direct me to a forum that discussions can occur? Cheers!
(in case it matters I'm also using Expressjs as the server).
Edit to add:
Part of my confusion is regarding the difference between 'io' and 'sock'. This code eg from the socket.io page is a good example of methods being applied to either of them:
io.on('connection', function(socket){
socket.emit('request', /* */); // emit an event to the socket
io.emit('broadcast', /* */); // emit an event to all connected sockets
socket.on('reply', function(){ /* */ }); // listen to the event
});
WebSocket server side listens for incoming socket connections from clients.
Each client upon connection opens its own socket between him and server. The server is the one that keeps track of all clients.
So once client emits the message server is listening for, the server can do with that message whatever. The message itself can contain information about who is the recipient of that message.
The server can pass the message to everyone or broadcast it to specific user or users based on information your client has sent you or some other logic.
For a card game:
The server listens for incoming connections. Once two clients are connected both of them should emit game ID in which they want to participate. The server can join their sockets in one game(Room) and all of the communication between those two clients can continue in that room. Each time one of the clients passes data to the server, that data should contain info about the recipient.
Here is one simple example that could maybe get you going:
Client side
// set-up a connection between the client and the server
var socket = io.connect();
// get some game identifier
var game = "thebestgameever";
socket.on('connect', function() {
// Let server know which game you want to play
socket.emit('game', game);
});
function makeAMove(move)
{
socket.emit('madeAMove', {move:move, game:game});
}
socket.on('move', function(data) {
console.log('Player made a move', data);
});
Server side
io = socketio.listen(server);
//listen for new connections from clients
io.sockets.on('connection', function(socket) {
// if client joined game get his socket assigned to the game
socket.on('game', function(game) {
socket.join(game);
});
socket.on('madeAMove', function(data){
let game = data.game;
let move = data.move;
io.sockets.in(game).emit('move', move);
});
})

Need to know something regarding socket.io and redis and nginx

My goal is to build a chat application - similar to whatsapp
To my understanding, socket.io is a real-time communication library written in javascript and it is very simple to use
For example
// Serverside
io.on('connection', function(socket) {
socket.on('chat', function(msg) {
io.emit('chat', msg);
});
});
// ClientSide (Using jquery)
var socket = io();
$('form').submit(function(){
socket.emit('chat', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat', function(msg){
$('#messages').append($('<li>').text(msg));
});
1) do I always need to start an io.on('connection') to use the real-time feature or i could just start using socket.on object instead? for example i have a route
app.post('/postSomething', function(req, res) {
// Do i need to start an io.on or socket.on here?
});
because i want the real-time feature to be listen only on specific route.
2) Redis is a data structure library which handles the pub/sub, why do we need to use pub/sub mechanism?
I read alot of articles but couldn't grasp the concept. Article example http://ejosh.co/de/2015/01/node-js-socket-io-and-redis-intermediate-tutorial-server-side/
for example the code below
// Do i need redis for this, if so why? is it for caching purposes?
// Where does redis fit in this code?
var redis = require("redis");
var client = redis.createClient();
io.on('connection', function(socket) {
socket.on('chat', function(msg) {
io.emit('chat', msg);
});
});
3) Just wondering why I need nginx to scale node.js application? i found this stackoverflow answer:
Strategy to implement a scalable chat server
It says something about load balancing, read that online and couldn't grasp the concept as well.
So far I have only been dealing with node.js , mongoose simple CRUD application, but I'm willing work really hard if you guys could share some of your knowledge and share some useful resources so that I could deepen my knowledge about all of these technologies.
Cheers!
Q. Socket.on without IO.on
io.on("connection" ... )
Is called when you receive a new connection. Socket.on listens to all the emits at the client side. If you want your client to act as a server for some reason then (in short) yes io.on is required
Q. Redis pub/sub vs Socket.IO
Take a look at this SO question/anwer, quoting;
Redis pub/sub is great in case all clients have direct access to redis. If you have multiple node servers, one can push a message to the others.
But if you also have clients in the browser, you need something else to push data from a server to a client, and in this case, socket.io is great.
Now, if you use socket.io with the Redis store, socket.io will use Redis pub/sub under the hood to propagate messages between servers, and servers will propagate messages to clients.
So using socket.io rooms with socket.io configured with the Redis store is probably the simplest for you.
Redis can act like a message queue if it is a requirement. Redis is a datastore support many datatypes.
Q. Why Nginx with Node.js
Node.js can work standalone but nginx is faster to server static content.
Since nginx is a reverse proxy therefore servers are configured with nginx to handle all the static data (serving static files, doing redirects, handling SSL certificates and serving error pages.
) and every other request is sent to node.js
Check this Quora post as well: Should I host a node.js project without nginx?
Quoting:
Nginx can be used to remove some load from the Node.js processes, for example, serving static files, doing redirects, handling SSL certificates and serving error pages.
You can do everything without Nginx but it means You have to code it yourself, so why not use a fast and proven solution for this.

Trying to broadcast socket.io message to all connected sockets in e.g. room

Challenge:
I would like to use SailsJS, and be able to join a room, by means of socket.io, and receive unsolicited messages from this room, not just when one enters or leaves the room but also receive objects.
So several clients connect to the server.
Afterwards broadcast (initiated from the server) messages/objects to every room and thus everey connected socket in this room.
I maybe could just send every connected socket a message, but dearly would like a example on how to do this with SailsJS 0.10, and a elegant method in the SailsJS 0.10 way.
I am looking at : https://github.com/balderdashy/sailsChat, but I think this is to close to the models themselves, with like e.g: autosubscribe: ['destroy', 'update']
In my opinion this should be more loosely coupled, and not so tied to the model itself.
Thanks in advance!
I.
The purpose of the SailsChat example is to demonstrate how Sails JS resourceful pubsub can take a lot of hassle out of socket messaging when you are mainly concerned with sending messages about models. The fact that you can make a full-featured chat app in Sails with very little back-end code is what makes it impressive. For situations where resourceful pubsub is not appropriate--or if you just plain don't want to use it--Sails exposes lower-level methods on the sails.sockets namespace. The docs are pretty clear on how they work.
To join a socket to an arbitrary room, do sails.sockets.join(<socket>, <roomName>), where <socket> is either a raw socket (probably from req.socket or a socket ID).
To broadcast a message to all sockets in a room, do sails.sockets.broadcast(<roomName>, <data>).
These and more methods are described in detail in the Sails JS documentation.
I'm just starting with SailsJS, and already a big fan.
I need to find out if this is also scalable with e.g. Heroku or other flavors of SAAS providers, but seems not that hard.
So just a follow up on what I did with SailsJS 0.10:
Server-side:
Made a controller with the following:
join: function (req, res) {
if (req.isSocket === true) {
sails.sockets.join(req.socket, 'mysecretroom');
return res.send(200, 'joined');
}
return res.send(200);
},
sendToRoom: function( req, res ) {
if (req.isSocket === true ) {
sails.sockets.broadcast('mysecretroom', 'messageevent', {message:'Listen very carefully, I'll shall say this only once..!'});
}
return res.send(200);
}
Client-side:
io.socket.on('messageevent', function (data) {
console.log(data);
})
+1 kudos #sgress454!
Thanks!

Nodejs and socket.io selecting sockets to broadcast

I have a nodejs + socket.io solution and sockets join different rooms. How can i select sockets from different rooms to broadcast to them a certain message without using a for loop that will stall the application for all users?
The solution is simple, make every "special" user (the ones that you want to group) connect to a secret channel with Socket.IO, in your server-side code:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
if (condition_to_determine_is_user_has_access) {
socket.join('specials');
socket.broadcast.to('specials').emit("new special user has joined");
}
});
You may also want to block access to that channel to un-allowed users, based on cookies / sessions.
Read more here: http://www.danielbaulig.de/socket-ioexpress/

Resources