Multiple socket.io instances at different paths - node.js

I'm making a REST API that works with routes and actions like /api/route/action. But I want to add WebSocket functionalities. So I want WebSockets to also be addressable by url.
I have this code:
const socketio = require('socket.io');
//server is a http.createServer()
module.exports = server => {
const io = socketio(server, { route: '/socketapi/test' );
io.on('connection', s => {
s.on('a', () => s.emit('b'));
s.emit('message', 'You connected to /test.');
});
const io2 = socketio(server, { route: '/socketapi/something_else' });
io2.on('connection', s => {
s.on('z', () => s.emit('y'));
s.emit('message', 'Hi');
});
};
The reason why I want to split them is so I don't have to keep track of event names I've already used, and so I can separate the logic in the connection event.
But it seems this is not possible. If I have two socket.io instances running I can't connect to either.
Is this possible or will I have to use some tricks and perhaps an event that the client can send to let me know what it wants to subscribe to?

You can use a built in feature of socket.io called namespaces to achieve this behaviour.
Here is a basic example:
Server side:
const nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
console.log('someone connected');
});
nsp.emit('hi', 'everyone!');
Client side:
const socket = io('/my-namespace');
Now the client can emit and receive messages which are specific to a namespace. With the use of namespaces your problem of name conflicts of the events, will be solved.

Related

Trigger `socket.io` event from server(`node.js`), not from client

I am trying to make a game server with node.js, socket.io.
The basic idea likes below.
Initialize socket.io instance when the server starts
Store instance in global scope, so controllers can access it
When API calls, we trigger some socket.io event in the controller or some other points
Here is the implementation I made ...
First, in server.js - entry point
let GlobalVars = require('./state/GlobalVars');
const apiRouters = require('./router');
...
app.use('/api', apiRouters);
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/test/simpleClient.html`)
});
const httpServer = http.createServer(app);
let socketIOInstance = socketIO(httpServer);
socketIOInstance.on('connection', (socket) => {
console.log('SOCKET.IO A USER CONNECTED');
socket.on('create', (data) => {
console.log('SOCKET.IO create called', socket);
socket.join(data.room);
socketIOInstance.emit('message', 'New people joined');
});
socket.on('join', (data) => {
console.log('SOCKET.IO join called', data);
})
socket.emit('message', 'Hi');
});
GlobalVars.socketIO = socketIOInstance;
// Add to global, so the controllers can manage own actions like create, join ...
httpServer.listen(port, () => {
console.log(`Server Listening on the port ${port}`);
})
...
When I access from a client, I am able to see SOCKET.IO A USER CONNECTED and Hi in the browser console.
Second, In api controller.
let GlobalVars = require('../state/GlobalVars');
...
router.post('/create', (req, res) => {
console.log('GenerateGameSokect');
let game = new Game();
let gameId = game.gameId;
// console.log('Global vars ', GlobalVars.socketIO);
GlobalVars.socketIO.emit('create', {
room: gameId
});
res.json({
result : 'SUCCESS',
game : game
})
});
I imported GlobalVars which contains socketIO instance. So what I expected was, socket create event triggered from the statement GlobalVars.socketIO.emit('create', Object) but could not find message in the server logs.
I got no clue what I was missing.
The final form I pursue is something like...
When user call create API, I creates socket connection and room
API will called in HTTP protocol, but in the API, the server publishes some events. - pubsub like.
Thanks for reading my questions b. Here is full source code till now(bitbucket public)
================== EDIT ====================
I got understood (maybe...)
The user-flow I wanted was ...
The client call API
(In the server) Checking validation in API and if valid emit to socket.io
If event accepted send new status to all clients
However, creating socket.io connection in the server looks strange for me, the solution is up to the client.
New user-flow I will change
The client call a validation API
If return is valid, the client emit socket.io event. This time server only do validation, not emit socket.io
In socket event, send new status to all other users
================== EDIT #2 ====================
This is a kind of conclusion. It looks I just misunderstanding the concept of socket communication. Like answer and replies say, Socket and HTTP are totally different channels, there is no way to connect both. (At least, without open new connection from http server to socket)
If this is wrong, you could add reply, Thanks
Now I understand you. Or at least I think!
Let's put it this way: there are two (asymetric) sides on a socket, server and client. What I called, respectively, "global manager" and "socket" in my comment to your post.
const server = require('socket.io')(yourHttpServer);
// client is installed as well when `npm i socket.io`
const client = require('socket.io-client')('http://localhost:' + yourServerPort);
// `socket` is the server side of the socket
server.on('connection', (socket) => {
// this will be triggered by client sides emitting 'create'
socket.on('create', (data) => {
console.log('a client socket just fired a "create" event!');
});
});
// this will be triggered by server side emitting 'create'
client.on('create', (data) => {
server.emit('create', {content: 'this will result in an infinite loop of "create" events!'});
});
In your /create route, when you GlobalVars.socketIO.emit('create', ...), the server-side socket handler isn't triggered, however if you have clients connected through a browser (or, like I showed above, if you connect a client socket directly from the server) then these will trigger their 'create' listener, if any.
Hope this helps you get on the right tracks!

NodeJs Socket.io Rooms

This is a pretty simple question but i want to make sure that i am scaling our socket.io implementation correctly. We are using socket.io to respond back to the client after a lengthy process on the nodejs backend. So basically client makes call, then socket.io signals the client that the process has completed. Also socket.io ONLY responds to a temporary room that was established for the request.
In nodejs i created a global variable for the following so that i could emit back to the client room:
global.io = require('socket.io')(server);
But to create the room itself I am a little unsure how to create it globally such that only the socket that connected and made the request receives the response.
So if i have 500 client machines that initiate a connection through socket.io, each one will have its own socket. To ensure that the rooms are unique i use a guid across all 500. Of course i do not want all sockets to receive traffic if only one socket for a specific room is supposed to be evaluating the emit....
any ideas?
If I understood your question correctly, you're looking to send information to that 1 socket?
Perhaps something like this:
socket.broadcast.to(socketId).emit('someeventname', eventData);
If you have the connection open with that client, that means you have their socket id through socket.id . You can emit events to just that socket.
const app = express();
var http = require("http");
var server=http.createServer(app).listen(2525, (req, res) => {
console.log("Server running on", 2525);
});
var socketIO = require("socket.io");
var io = socketIO(server);
global.io = io
io.on("connection", async (socket) => {
socket.on("joinrooms", async (data) => {
socket.join(data.userId);
});
socket.on("sendMessage", async (data) => {
console.log("message", data);
io.to(data.touserId).emit("sendMessage", data);
});
});
/* Must Read section
Joinrrom data sample
data={
userId:123 //User's unique id.
}
sendMessage data sample
data={
userId:123, //sender User's unique id.
touserId:456, //reciver User's unique id.
}
Here I'm creating a room from the user's unique id(stored in DB) so whenever I
want to send data to a particular user I will emit an
event("io.to(data.touserId).emit") using the user's
a unique id that way only specific users will get messages.
*/

connect 2 websockets ws and socket.io

I'm getting data from a web page with websocket but I need to deploy it with socketIO to client. In my server.js client connects with socketio well but after that data(line1 line2) can't comes properly, always I need to restart server 2-3 times.Then it comes.
Here is my declerations
var server = require('http').Server(app);
var io = require('socket.io')(server);
const WebSocket = require('ws')
const wss = new WebSocket('gettingDataAdress');
io.on('connection', function (socket) {
console.log("client connected");
wss.on('open', () => {
console.log("send processing");
//line1
})
wss.on('message', () => {
console.log("getting message processing");
//line2
})
After restarting my server.js 2-3 times it can comes to line1 and line2 , it can't directly.However whenever I comment the socketio Part(I mean only websocket working) it works perfect.How can I do that ? Thank you
You are using two different websockets ws and socket.io. Use only one to connect to the client and subscribe to the coming messages
Only Socket.io
io.on('connection', function (socket) {
console.log("client connected");
socket.on('open', () => {
console.log("send processing");
//line1
})
socket.on('message', () => {
console.log("getting message processing");
//line2
})
Only ws
const WebSocket = require('ws');
const ws = new WebSocket('url');
ws.on('open', () => {
//do processing
});
ws.on('message', () => {
//do processing
});
wanted to write a comment but had no reputation to write, so decide write on here. sorry!
If you want to subscribe, rather use socket.io-client. However, socket-io itself is not a proper library to subscribe wss.
https://github.com/socketio/socket.io-client/issues/1208
actually, Socket.IO is not a WebSocket implementation, it has its own protocol which may use a WebSocket connection to transmit data
-darrachequesne-(most contributor of socket-io client library)
so If you get data from wss page, then use ws library and spread it by socket-io. I believe that what you are doing is pretty fine. might need to fix a bit though.
Connecting to GDAX websocket api using socket.io with webpack
there is similar question to get data from ws.

How to create multiple Nodejs socket io server-client?

I am new to Nodejs and Socket.io, and this is first time when I am creating any chat application, so pardon me if I am asking some silly question.
In my web I have to kind of chat services, one is Live debate which is kind of chat room, and another one is private messaging like Facebook Messenger.
I have created both, in private messenger before showing the message I am checking the conversation Id, it's working quite appropriately. Live debate is also working appropriately.
But there is a issue, any message sent in private messenger displays in live debate window also. So I change the Server.js file for messenger and also changed the listening port, now the listening port for live debate is 3000 and for messenger is 8050, but still Live debate receiving the messenger messages.
Am I doing this in wrong way? Is there any other way to run two chat applications ?
I am using this server code
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
server.listen(3000);
io.on('connection', function (socket) {
console.log("client connected");
var redisClient = redis.createClient();
redisClient.subscribe('message');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
socket.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
With using Namespace
server.js
var nsp = io.of('/debate')
nsp.on('connection', function (socket) {
console.log("client connected"+socket.id);
var redisClient = redis.createClient();
redisClient.subscribe('message');
var redisClient1 = redis.createClient();
redisClient1.subscribe('debate');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
nsp.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
client code
var socket = io.connect('http://localhost:3000/debate');
socket.on('message', function (data) {
data = jQuery.parseJSON(data);
console.log(data.user);
$( "#messages" ).append( "<strong>"+data.user+":</strong><p>"+data.message+"</p>" );
$('#messages').animate({
scrollTop: $('#messages').get(0).scrollHeight}, 200);
});
socket.io supports the use of different namespaces. You should use that feature instead of creating two individual servers. After that you can use socket.emit to that specific namespace. For more information see the documentation: https://socket.io/docs/rooms-and-namespaces/
It's not too difficult, I wrote a chat app, as I think everyone does when they start nodejs, but nodeJS has rooms which are quite easy to use.
io.on('connection', function(client){
client.on('room_connection', function(id){
client.join(id); // User joins room.
})
client.on('message', function(data){
io.to(data.room).emit('message', {message:data.message, client:client.conn.id});
});
})
This is pretty much all you need. This works for PM's since u simply won't allow multiple users to join this chatroom, and normal chatrooms which multiple users can join.

Socket.io Best coding practice

I'm developing a Node.js application which uses Socket.io to handle the real time communication. My code is full of On and Emit functions. I use room feature as well. my app looks like this:
var server = require('http').Server();
var io = require('socket.io')(server);
io.on('connection', function(socket){
socket.on('event1', function(data){"lots of socket and non-socket codes" });
socket.on('event2', function(data){"lots of socket and non-socket codes" });
socket.on('event3', function(data){"lots of socket and non-socket codes" });
socket.on('disconnect', function(){"Some other code"});
});
server.listen(portNum);
it works fine but It is not my ideal solution.
Firstly, in this approach everything is in one big file instead of smaller file with isolated functionality.
secondly, it is quite difficult to debug and maintain this app as it is quite messy when it comes to 1000+ lines of code.
Here is my question:
Is there a preferred/best practice on developing enterprise quality Socke.io applications?
If yes, Is there any large opensource Socket.io application that demonstrait this approch or any article which help me to refactor my code in a better fassion?
I think starting by putting every callback function in a different function that you put out of io.on('connection'), and maybe also put them in a different file (using module.exports), you will start having a clearer application.
Ok so i will write you one possibility that i use, i don't know if it's the best pattern of the universe for socket.io, but that's good i think.
In your main file (the file with io.onconnection), you can have something like this (you dont have to use namespace, it's just an example) :
var SocketEvent = require('./socketEvent');
io.of('/user').on('connection', function (socket) {
SocketEvent.load_common_event(socket);
SocketEvent.load_user_event(socket);
});
io.of('/operator').on('connection', function (socket) {
SocketEvent.load_common_event(socket);
SocketEvent.load_operator_event(socket);
});
And in the socketEvent.js that you load you can have this :
exports.load_common_event = function(socket){
socket.on('disconnect', function(){"Some other code"});
};
exports.load_user_event = function(socket){
socket.on('event1', function(data){"lots of socket and non-socket codes" });
socket.on('event2', function(data){"lots of socket and non-socket codes" });
socket.on('event3', function(data){"lots of socket and non-socket codes" });
};
exports.load_operator_event = function(socket){
socket.on('event4', function(data){"lots of socket and non-socket codes" });
socket.on('event5', function(data){"lots of socket and non-socket codes" });
socket.on('event6', function(data){"lots of socket and non-socket codes" });
};
Let me know if you have any question
Add-on
If you want something like Socket.on('event' , myModule.doSomething);
you can do like this i guess in the module :
client :
var myModule = require('./socketModule');
io.on('connection', function (socket) {
socket.on('event' , myModule.doSomething(/*some parameters (socket)*/));
});
server socketModule.js :
exports.doSomething = function(/*some parameters (socket)*/){
/* Some processing around */
};
For those who are wondering about pass paremeters, .bind could be an option
const events = require('./events.js')
io.of('/chat').on('connection', (socket) => {
socket.on('print', events.print.bind({socket, io}))
})
event.js
const util = require('util')
function print(data) {
console.log(util.inspect(this.socket).substr(0, 100))
console.log(util.inspect(this.io).substr(0, 100))
}
module.exports = {
print
}
More use of pages, less code in each, share them independently when needed:
Note I assume you are working with express. If not, just remove express from my demos.
// app.js
var app = require('express')();
module.exports = app;
// server.js
var app = require('./app');
var server = require('http').createServer(app);
module.exports = server;
//socket-io-redis.js
var server = require('../server');
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
io.set('origins', '*:*');
module.exports = io;
Now on every page on your API or on your server you can just import the Io instance (reference and efficient) like this:
var Io = require('../path/to/io-config-file');
console.log(Io); // object
Io.to(room).emit(event, payroll);
You can also make a main connection file like this:
Note I'm pasting here the exact code that I actually use, just in order to show you some of my best practices (of course, adapt and adjust this as your needs):
var { Io } = require('#dependencies/_index');
var Defaults = require('#helpers/defaults');
var _index = require('./services/_index');
const {
validate,
get_user,
subscribe
} = _index;
Io.on("connection", socket => {
socket.on("join", async info => {
await validate(info);
await subscribe(await get_user(info), info, socket, 'join');
});
socket.on("leave", async info => {
await validate(info);
await subscribe(await get_user(info), info, socket, 'leave');
});
});
In the example above, I listen for new connections and securely verified subscribe them to the respective rooms. I am using services in order to so - they are independent as I said that's how all your code should be like.

Resources