How can I export socket.io connection to another controller - node.js

I am stuck in a problem.
I am making a Socket.IO connection in the bin file which is working, but can anyone tell me how I can export this connection to different controller. This is my bin file code.
var app = require('../app');
var debug = require('debug')('userservice:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3015');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
var io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('Connection made #######################################################.', socket.id);
socket.on('disconnect', () => {
console.log('Connection disconnected #######################################################.', socket.id);
});
});
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

There is plenty of techniques can be used to re-use the socket instance, an easy and simple one is to create a singular class, to be able to:
Initiate socket instance
Export the instance to other modules
socket.js:
let io;
module.exports = {
init: (server) => {
io = require('socket.io').listen(server); io.origins('*:*');
return io;
},
get: () => {
if (!io) {
throw new Error("socket is not initialized");
}
return io;
}
};
server.js:
const app = require('../app');
const http = require('http');
/**
* Get port from environment and store in Express.
*/
const port = '3015';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
const io = require('./socket.js').init(server);
io.on('connection', (socket) => {
console.log('Connection success', socket.id);
socket.on('disconnect', () => {
console.log('Connection disconnected', socket.id);
});
}
Now you can use it in other modules.
const io = require('./socket.js').get();

You could just create a socket.io controller module that exports a function that you call for every new connection.
So, in your current server file, you add this:
const {socketConnected} = require('socketController.');
And, you modify this portion to call it for each new socket:
var server = http.createServer(app);
var io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('Connection made #######################################################.', socket.id);
// tell controller about new socket (and pass the io instance)
socketConnected(socket, io);
socket.on('disconnect', () => {
console.log('Connection disconnected #######################################################.', socket.id);
});
});
Then, your socket controller can be like this:
module.exports.socketConnected = function(socket, io) {
// new socket.io socket connected
console.log(`controller: got socket.io connection ${socket.id}`);
// register appropriate event handlers on the socket here
}

We can export the socket module in every file by creating a global object in such a way.
let io;
const connectedUsers = [];
const setupSocketIO = function (server) {
io = require('socket.io')(server, { cors: { origin: '*' } });
io.on('connection', function (socket) {
connectedUsers[connectedUsers.length] = socket.id;
socket.on('getConnectedUsers', () => {
io.sockets.emit('returnConnectedUsers', connectedUsers.length);
});
socket.on('disconnect', () => {
let socketIdToRemoveIndex = -1;
for (let i = 0; i < connectedUsers.length; i++) {
if (connectedUsers[i] === socket.id) {
socketIdToRemoveIndex = i;
}
}
if (socketIdToRemoveIndex !== -1) {
connectedUsers.splice(socketIdToRemoveIndex, 1);
}
io.sockets.emit('connectedUsers', connectedUsers.length);
});
});
};
const Socket = function () {
return {
emit: function (event, data) {
io.sockets.emit(event, data);
},
to: function (roomId, event, data) {
io.sockets.to(roomId).emit(event, data);
},
};
};
exports.setupSocketIO = setupSocketIO;
exports.Socket = Socket;
And in file or componenet, we want to use.
const getAllProjects = async (req, res) => {
let Socket = require('../sockets').Socket();
Socket.emit('SOCKET_PUSH_NOTIFICATION', { data: 'This is random data' });
Socket.to('SOCKET_PUSH_NOTIFICATION', 'event', { data: 'Muhammad' });
}

Related

How to use socket io instance on multiple files in express.js app

In my express app, generated with express-generator, I want to use the io of socket.io in some other controller files to emit data to client sockets. My approach is below, but I get the following error with that. It would be a great favor if someone can help me in this case.
(node:11376) UnhandledPromiseRejectionWarning: TypeError: io.emit is not a function
at F:\backend\controllers\LessonController.js:169:9
In the express apps, generated by express-generator, the process of creating the server happens in the /bin/www.js. I tried importing the io instance from there and use it in some other file, but it didn't work.
bin/www.js
#!/usr/bin/env node
var app = require('../app');
var debug = require('debug')('backend:server');
var http = require('http');
var port = normalizePort(process.env.PORT || '8080');
app.set('port', port);
var server = http.createServer(app);
const io = require('socket.io')(server);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
// several other functions are omitted for brevity
module.exports = io;
LessonController.js
const Lesson = require('../models/Lesson');
const Course = require('../models/Course');
const User = require('../models/User');
const io = require('../bin/www')
var _ = require('lodash');
module.exports = {
addComment: async (lessonId, userId, content, callback) => {
const newData = {
comments: {
user: userId,
content: content,
},
};
Lesson.findOneAndUpdate({ _id: lessonId }, { $push: newData }, {new: true})
.exec()
.then(
function (data) {
if (data) {
io.emit("comment_"+lessonId,data)
callback(null, data);
} else if (err) {
callback(err, null);
}
}
)
}
};
You can try to export the socket.io instance to the global level and access that as needed.
My project was also created with express-generator, therefore, follows the same template.
In my project, I would like to count the current number of active users in home page.
Here is an example:
bin/www
#!/usr/bin/env node
const app = require('../app');
const http = require('http').Server(app);
const io = require('socket.io')(http)
http.listen(process.env.PORT);
io.on('connection', (socket) => {
const qtd = socket.client.conn.server.clientsCount;
io.emit('novaconexao', qtd);
socket.on('disconnect', () => {
io.emit('disconnecteduser', qtd - 1);
});
});
app.set('socketio', io);//here you export my socket.io to a global
console.log('Microsservice login listening at http://localhost:%s', process.env.PORT);
server/index.js
const router = require('express').Router();
router.get('/', (req, res) => {
const io = req.app.get('socketio'); //Here you use the exported socketio module
console.log(io.client.conn.server.clientsCount)
io.emit('new-user', {qtd: io.client.conn.server.clientsCount})
res.status(200).json({ msg: 'server up and running' });
})
module.exports = router;
Following this strategy, you can use socketio in any route in your application.
Here is a solution
Create a module io.js
const sio = require('socket.io');
let io = null;
module.exports = {
//Initialize the socket server
initialize: function(httpServer) {
io = sio(httpServer);
io.on('connection', function(socket) {
console.log('New client connected with id = ', socket.id);
socket.on('disconnect', function(reason) {
console.log('A client disconnected with id = ', socket.id, " reason ==> ", reason);
});
});
},
//return the io instance
getInstance: function() {
return io;
}
}
In bin/www.js
var server = http.createServer(app);
require('path_to_io_js/io').initialize(server);
In your controllers / LessonController.js
//require the io module
const socket = require('path_to_io_js/io');
module.exports = {
addComment: async (lessonId, userId, content, callback) => {
const newData = { comments: { user: userId, content: content, }, };
Lesson.findOneAndUpdate({ _id: lessonId }, { $push: newData }, { new: true })
.exec().then(function (data) {
if (data) {
//get the io instance
const io = socket.getInstance();
io.emit("comment_" + lessonId, data)
}
callback(null, data);
}).catch(err => {
callback(err);
})
}
};
Create socketInstance.js
let io = null;
// set this when you initialize the io
const setSocketInstance = (ioInstance) => {
io = ioInstance;
};
// you can call this anywhere
const getSocketInstance = () => {
return io;
};
inside socket.js where you initialize io
const setSocketInstance = require("./socketInstance");
const initializeIO = (server) => {
const io = require("socket.io")(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
},
});
// as soon as we initialize the io, we set the instance
setSocketInstance(io);
// ....
};
Now you can call getSocketInstance anywhere in your app.

SocketIO – connection issues and clustering

I have a really simple NodeJS app that I want to run on Heroku. This is how the index.js file looks like:
Server (port 3030)
const http = require('http');
const os = require('os');
const express = require('express')
const throng = require('throng'); // For cluster management
const { port, env, isProduction } = require('./config/vars');
const SocketIO = require('socket.io');
// Setting up a simple express app and wrapping it with http server
const setupServer = () => {
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
const server = http.createServer(app);
return server;
};
const setupSocket = (server) => {
const io = new SocketIO(server);
io.on('connection', (socket) => {
console.log(`[Socket] Connection established: ${socket.id}`);
socket.on(msg.rooms.join, (room) => {
socket.join(room);
socket.to(room).emit(msg.rooms.joined);
console.log(`[Socket] User ${socket.id} joined '${room}' room`);
});
socket.on('disconnect', () => {
console.log(`[Socket] Distonnected: ${socket.id}`);
});
});
return io;
};
const WORKERS = (() => {
if (!isProduction) return 1;
return process.env.WEB_CONCURRENCY || os.cpus().length;
})();
async function master() {
console.log(`Preparing ${WORKERS} workers...`);
console.log('Master started.');
}
// There should be one server instance for each worker
const start = () => {
const server = setupServer(); // Returns and `http` server instance
const socket = setupSocket(server);
server.listen(port, async () => {
Logger.info(`Server – listening on port ${port}`);
});
return server;
};
const instance = throng({
workers: WORKERS,
lifetime: Infinity,
start,
master,
});
module.exports = instance;
Client (port 3000)
const setupSocket = ({ room }) => {
// Fallback if already setup
if (window.sockets[room]) {
return window.sockets[room];
}
const socket = io('http://localhost:3030');
socket.on('connect', () => {
console.log('[Socket] Connection established!', socket.id);
socket.emit('room.join', room);
});
socket.on('room.joined', () => {
console.log(`[Socket] Connected to ${room} room!`);
});
window.sockets[key] = socket;
return socket
};
The problem – the connection is sometimes established properly but most of the time I get an error
Error during WebSocket handshake: Unexpected response code: 400
What might be the problem here? Is it because I have it on two different ports or is it because of the clusters?
I've tried removing the throng part of the code, and just calling start() method without any cluster setup, but the problem remains :(
why would you use http module? The server instance that you send in the socketIO constructor should be the return object of the expressInstance.listen
Something more like this:
const express= require('express')
const app = express()
const socketio = require('socket.io')
app.use(express.static(__dirname + '/public'))
const server = app.listen('4000',()=>{
console.log('Listening to port:4000')
})
const io = socketio(server)
io.on('connect',(socket)=>{
socket.broadcast.emit('new_user')
socket.on('new_message',(message)=>{
io.emit('new_message',message)
})
})
source code: socket-io chat

Socket.io in server gets disconnect repeatedly while on client side doesn't

I am going to make a private chat app like WhatsApp.
I connect to the server successfully
but the socket after several seconds gets disconnect from the server.
while on the client it doesn't disconnect.
Server code:
const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const port = 3000;
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
const onlineusers = {};
const socketid = {};
io.on('connection', cs => {
cs.on('online', username => {
if(username){
onlineusers[username] = cs.id;
socketid[cs.id] = username;
}
console.log("\nonline: ", onlineusers);
});
cs.on('disconnect', () => {
delete onlineusers[socketid[cs.id]];
console.log("\noffline: ", onlineusers);
});
});
const chat = io.of("/chat");
chat.on('connection', cs => {
cs.on('startchat', username => {
if (username){
chat.to('/chat#'+onlineusers[username]).emit('hey', 'I love programming');
}
});
});
server.listen(port, err => {
if(err){
console.error("Some Error: "+err);
}else{
console.log(`Server is running on port: ${port}`);
}
});
MY CLIENT code is by react-native and socket.io-client:
On line users file:
import io from 'socket.io-client';
const SocketEndpoint = 'http://192.168.43.172:3000';
this.socket = io(SocketEndpoint, {
transports: ['websocket']
});
this.socket.on('connect', () => {
if (this.state.username) {
this.socket.emit("online", this.state.username);
}
});
this.socket.on('connect_error', (err) => {
Alert.alert(err);
});
this.socket.on('disconnect', () => {
Alert.alert('disconnected');
});
Chat page file:
import io from 'socket.io-client';
const SocketEndpoint = 'http://192.168.43.172:3000/chat';
this.socket = io(SocketEndpoint, {
transports: ['websocket']
});
this.socket.on('connect', () => {
if (theirusername) {
this.socket.emit('startchat', theirusername);
}
this.socket.on('hey', data => {
alert(data);
});
this.socket.on('janajan', data => {
alert(data);
});
});
I want to keep to client socket on the server until the client themselves gets the disconnect.
because here when I want to say hey it gets a disconnect and my message could pass to the client.
thank you before

How to use (socket.io)emit function outside of socket function - nodejs,express

I have the following code
var app = require('express')();
var http = require('http');
var server = http.createServer();
var socket = require('socket.io');
var io = socket.listen( server );
io.sockets.on('connection', function(socket) {
console.log('socket connected');
socket.broadcast.emit('newUser', 'New User Joined, Say Hi :D');
socket.on('serverEmit',function(msg) {
console.log('works');
});
socket.on('chatMessage', function(msg) {
io.emit('server_emit', msg);
console.log(msg);
});
});
server.listen(3500, function() {
console.log('listening on *:3500');
});
So my question is how to add an emit function outside of this socket connection. For example, if I have a get request like below
app.get('/link',function(req,res) {
io.sockets.emit('trigger','triggered'); // Process I want to make
});
Thanks in advance.
You need to export your io first so that it can be reusable.
socket-setup.js
const socket = require("socket.io")
let _io;
const setIO = (server) => {
_io = socket(server, {
cors : {
origin : "*",
headers : {
"Access-Control-Allow-Origin" : "*"
}
}
})
return _io
}
const getIO = () => {
return _io
}
module.exports = {
getIO,
setIO
}
Then in your app entry file (index.js), setup your io.
const app = express()
const server = http.createServer(app)
let io = setIO(server)
io.on("connection", socket => {
//Your job
})
Then wherever, e.g. message.js you want to emit event. You can use io like this.
const onMessageRecieved = () => {
try {
getIO().emit("hello", "Bye")
} catch (error) {
console.log(error);
}
}
That's it. Enjoy.
You almost had it:
it's io.emit('trigger','triggered');
And if you need to emit to a namespace you can do:
const namespace = io.of("name_of_your_namespace");
namespace.emit('trigger','triggered');

Socket.io doesn't emit from an app? (ionic)

I use node.js as server and my ip is example.com:4000. So my endpoint of socket on server side should be example.com:4000. I manage to get it work even when I try to emit something from localhost to example.com:4000.
But when I have an app (I'm using ionic with a real device) but the socket seems didn't send it out the emit of socket.io. I wonder why, it has been 2 days I try to debug this issue.
my code on server side
var server = require("http").Server(express);
var io = require("socket.io")(server);
server.listen(400);
io.on('connection', function(client) {
client.on('msg', function(data) {
console.log(data)
});
});
client side
//angular service
.factory('socket',function(socketFactory){
var myIoSocket = io.connect('http://example.com:4000');
mySocket = socketFactory({
ioSocket: myIoSocket
});
return mySocket;
});
//within my controller
socket.emit('msg',data);
To summarize, things worked on localhost but not when I try on an app?
Try to run this code, since I know that example is working:
Factory
MyApp.factory('socket', function ($rootScope) {
var socket = io.connect('http://localhost:9000');
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
}
};
});
Controller
MyApp.controller('MainCtrl', function($scope, socket){
$scope.users = [];
socket.emit('connection'); });
...
Server side
"use strict";
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket) {
console.log('connected!');
socket.on('disconnect', function() {
console.log('user disconnected');
});
});
server.listen(9000, function() {
console.log('server up and running at 9000 port');
});

Resources