I am looking to separate my Socket.io events/init from my main node server logic. The goal is to not only maintain the Socket.io code, but also maintain any events that I may emit.
I'm a little overwhelmed with understanding the scope of this and the method of encapsulation with Node and was hoping to get some clarification on a better way of doing things.
server.js
//-- Socket.io
var http = require('http').Server(app);
//-- Encapsulate Socket init/events
var io = require('./socket.js').listen(http)
var api = express.Router();
api.route('/users/:user_id')
.put(function(req, res) {
io.dispatch(req.body.event);
}
}
//-- Start Server
http.listen(port);
console.log('Server started on port: ' + port);
io.init(app);
socket.js
'use strict'
var io = require('socket.io');
var ioJwt = require('socketio-jwt');
var clientsConnected = 0;
var ioServer;
exports.listen = function(http, app) {
ioServer = io.listen(http);
return ioServer;
}
exports.init = function(app) {
ioServer.use(ioJwt.authorize({
secret: app.get('secret'),
handshake: true
}));
ioServer.on('connection', function(socket){
connect();
socket.on('error', function(err) {
console.log('Socket.IO error:');
console.log(err);
});
socket.on('disconnect', function(){
disconnect();
});
});
}
function connect() {
clientsConnected++;
console.log('user connected. ' + clientsConnected + ' total.');
}
function disconnect() {
clientsConnected--;
console.log('user disconnected. ' + clientsConnected + ' total.');
}
The minimal communication the two modules need to have is that the http server has to be provided to the sockets module so it can initialize itself properly and the http server must call the sockets module to dispatch appropriately. So, you can pursue modularization that has only those two connections:
server.js
var http = require('http').Server(app);
var dispatch = require('./sockets.js')(http);
var api = express.Router();
api.route('/users/:user_id')
.put(function(req, res) {
dispatch(req.body.event);
}
}
//-- Start Server
http.listen(port);
console.log('Server started on port: ' + port);
sockets.js
'use strict'
var io = require('socket.io');
var ioJwt = require('socketio-jwt');
var clientsConnected = 0;
var ioServer;
module.exports = function(server) {
ioServer = io.listen(server);
ioServer.use(ioJwt.authorize({
secret: app.get('secret'),
handshake: true
}));
ioServer.on('connection', function(socket){
connect();
socket.on('error', function(err) {
console.log('Socket.IO error:');
console.log(err);
});
socket.on('disconnect', function(){
disconnect();
});
// return dispatch function
return function(data) {
io.dispatch(data);
}
}
function connect() {
clientsConnected++;
console.log('user connected. ' + clientsConnected + ' total.');
}
function disconnect() {
clientsConnected--;
console.log('user disconnected. ' + clientsConnected + ' total.');
}
Related
How to solve this? I don't get it,,please help, thanks. And this is the error message when I run node server.js:
client.on('getRelay', function (state) {
ReferenceError: client is not defined
var express = require ('express');
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = 3000;
let bodyRelay = {
relayId: 'RELAY-123',
state: 1
}
app.use(express.static(__dirname + '/node_modules'));
app.get('/', function(req, res, next) {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(client) {
console.log('a user has connected');
});
io.sockets.emit('setRelay', bodyRelay);
client.on('getRelay', function (state) {
bodyRelay = state;
console.log('id: ' + client.id + 'bodyRelay: ' + JSON.stringify(bodyRelay));
// io.sockets.emit('setRelay', bodyRelay);
});
client.on('disconnect', function() {
console.log('a user has disconnected');
});
server.listen(port, () => {
console.log('Server is running on port: ' + port)
});
The blocks of code that reference client, must be inside this callback:
io.on('connection', function(client) {
console.log('a user has connected');
});
because that is where client is defined. Like this:
io.on('connection', function(client) {
console.log('a user has connected');
client.on('getRelay', function (state) {
bodyRelay = state;
console.log('id: ' + client.id + 'bodyRelay: ' + JSON.stringify(bodyRelay));
// io.sockets.emit('setRelay', bodyRelay);
});
client.on('disconnect', function() {
console.log('a user has disconnected');
});
});
A few other notes on your code.
The location you have this:
io.sockets.emit('setRelay', bodyRelay);
does not make any sense because you're calling it when you initialize the module which will NEVER have any connected socket.io clients so it will not do anything useful.
And, the way you are using the bodyRelay variable seems like a possible design error. You've made it a module-level variable which means it is shared among all request handlers for all connections. Unless this is intended to be something that all clients can change and all clients access the exact same set of data, then it perhaps needs a different design. I don't know what you're really trying to do with it to know what design to suggest.
to my server are just two clients allowed to connect with. How to set the max of clients on two clients of socket.io?
this is how my server.js looks like:
var path = require('path');
var express = require('express'),
app = express(),
http = require('http'),
socketIo = require('socket.io');
var server = http.createServer(app);
var io = socketIo.listen(server);
server.listen(9000);
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname + 'index.html'));
});
app.use(express.static(__dirname + '/'));
io.on('connection', function (socket) {
socket.on('data', function (data) {
socket.broadcast.emit('data', data)
});
socket.on('disconnect', function () {
console.log("disconnect")
});
});
This is untested, but you should be able to check number of users on connection and change it on connect/disconnect.
Here is an example:
const connectedUsers = 0;
const maxUsers = 2;
io.on('connection', function (socket) {
if(connectedUsers + 1 > maxUsers){ // check if the new connection will exceed the max connections allowed
socket.disconnect(); // if so, disconnect the user and exit https://stackoverflow.com/a/5560187/11518920
return;
}
connectedUsers++; // otherwise + 1 to the connectedUsers
socket.on('data', function (data) {
socket.broadcast.emit('data', data)
});
socket.on('disconnect', function () {
connecteUsers--; // on a disconnected decrease the connectedUsers count
console.log("disconnect")
});
});
I have two applications hosted in openshift v2, both have the same code, the first uses Node.js 0.10 and socket.io works perfectly. The second application uses Node.js 8.2.1 but socket.io doesn't work and I get the same error as in these other sites:
Client side receive polling on socket.io
Socket.io cannot connect, resorts to “polling”
Socket.io connection reverts to polling, never fires the 'connection' handler
I tried to make the answers, but without result, if the code is the same in my two applications.. What can be failing? It's necessary to make some changes in the new version of Node.js?
This is the relevant code in my application and information about it:
Both app run perfectly on ports 8080 or 3000.
SERVER
App.js
var fs = require('fs');
var express = require('express');
var app = express();
var server = require('http').Server(app);
var bodyParser = require ('body-parser');
var jwt = require('jsonwebtoken');
var io = require('socket.io')(server);
var server_ip_address = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';
var server_port = process.env.OPENSHIFT_NODEJS_PORT || 3000;
server.listen(server_port, server_ip_address, function () {
console.log( "APP Listening on: " + server_ip_address + ":" + server_port )
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.static(__dirname + '/'));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function (socket) {
console.log('USUARIO CONECTADO');
socket.on('coordenada', function (data) {
io.emit('notificacion', {
Latitud: data.latitudData,
Longitud: data.longitudData,
Nombre: data.name
});
});
});
CLIENT
Controllers.js
app.controller('mapCtrl', function ($scope, NgMap, socket) {
vm = this;
socket.on('notificacion', function(data) {
console.log("coordenada recibida: ", data.Latitud +","+ data.Longitud +" de "+ data.Nombre);
var name = data.Nombre;
vm.transportistas = [
{id:'1', nombre: data.Nombre, posicion:[data.Latitud, data.Longitud]}
];
NgMap.getMap().then(function(map) {
vm.map = map;
google.maps.event.trigger(vm.map, 'resize');
});
});
vm.wayPoints = [
{location: {lat:39.502223, lng: -0.363244}},
];
NgMap.getMap()
.then(function(map) {
vm.map = map;
vm.map.setZoom(6);
vm.map.setCenter(new google.maps.LatLng(40, -4));
});
vm.mostrarDetalles = function(e, transportista) {
vm.transportista = transportista;
vm.map.showInfoWindow('foo-iw', transportista.id);
};
});
factorias.js
app.factory('socket', ['$rootScope', function($rootScope) {
var socket = io.connect();
return {
on: function(eventName, callback){
socket.on(eventName, callback);
},
emit: function(eventName, data) {
socket.emit(eventName, data);
}
};
}]);
And I get this on web console ONLY in the app with Node.js 8.2.1:
GET http://localhost/socket.io/?EIO=3&transport=polling&t=1448850081140-15
GET http://localhost/socket.io/?EIO=3&transport=polling&t=1448850080247-12
GET http://localhost/socket.io/?EIO=3&transport=polling&t=1448850080252-13
etc...
**I'm sorry for my English.
I'm new to nodejs and socket io thing, but I'm really trying to understand how this works. I've already completed the "chat tutorial" from the socket io website. However, I'm trying to do something "new" here. Each time a new connection is established, I want to broadcast an event to all clients, by appending a new tab with a random number. However, the only things that happens, is that it appends once (on my own connection) but nothing to others? Is this not possible, or am i just doing it the wrong way?
Server,
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use("/assets/stylesheets", express.static(__dirname + '/assets/stylesheets'));
app.use("/assets/javascripts", express.static(__dirname + '/assets/javascripts'));
app.use("/assets/demo/avatars", express.static(__dirname + '/assets/demo/avatars'));
app.use("/assets/fonts/font-awesome/", express.static(__dirname + '/assets/fonts/font-awesome/'));
app.get('/dashboard', function(req, res){
res.sendFile(__dirname + '/dashboard.html');
});
io.on('connection', function(socket) {
socket.on('new user', function(username) {
socket.emit('new connection', { nickname: username });
console.log('new connection ' + username);
});
});
http.listen(3000,function() {
console.log('Listening *:3000');
});
Client,
<script type="text/javascript">
var randomNumber = Math.floor((Math.random() * 10) + 1);
var socket = io();
socket.on('connect', function() {
socket.emit('new user', randomNumber);
});
socket.on('new connection', function(data) {
var nickname = data.nickname;
$('<li class="active"> ' + nickname + '<span class="label label-success">12</span> </li>').appendTo("#uidemo-tabs-default-demo");
});
</script>
You need to keep a list of all the open connections on the server and then, when a new connection occurs, send messages to all the other connections.
Here is modified code
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var connections = [];
app.use("/assets/stylesheets", express.static(__dirname + '/assets/stylesheets'));
app.use("/assets/javascripts", express.static(__dirname + '/assets/javascripts'));
app.use("/assets/demo/avatars", express.static(__dirname + '/assets/demo/avatars'));
app.use("/assets/fonts/font-awesome/", express.static(__dirname + '/assets/fonts/font-awesome/'));
app.get('/dashboard', function(req, res){
res.sendFile(__dirname + '/dashboard.html');
});
io.on('connection', function(socket) {
socket.on('new user', function(username) {
connections.forEach(function (socket) {
try {
socket.emit('new connection', { nickname: username });
} catch (err) {
console.log('connection probably closed: ', err);
}
});
console.log('new connection ' + username);
connections.push(socket);
});
});
http.listen(3000,function() {
console.log('Listening *:3000');
});
However, be aware that this will only work if your Node.js server only has one instance (i.e. you are not using the cluster module), more work will be required to get it to work in a cluster and even more if it must work in a redundant infrastructure.
You have to use socket.broadcast.emit to broadcast..
socket.on('new user', function(username) {
socket.broadcast.emit('new connection', { nickname: username });
console.log('new connection ' + username);
});
However this will not send the message to your own connection but to all other connections. If you want to send the message to all connections including yourself,
socket.on('new user', function(username) {
socket.emit('new connection', { nickname: username });
socket.broadcast.emit('new connection', { nickname: username });
console.log('new connection ' + username);
});
I am trying to build a small app in nodejs to publish and subscribe. I am stucked in how I can publish from client side. Here is the code I have.
Here is my server code (server.js)
var express = require('express'),
app = express(),
http = require('http'),
server = http.createServer(app);
app.use(express.bodyParser());
app.get('/', function(req, res) {
res.sendfile(__dirname + '/public/index.html');
});
app.post('/publish/:channel/:event/', function(req, res) {
console.log("**************************************");
var params = req.params;
console.log(req.params);
console.log(req.body);
var data = req.body;
console.log("**************************************");
var result = io.sockets.emit(params.channel,{event:params.event,data:data});
//console.log(result);
console.log("**************************************");
res.sendfile(__dirname + '/public/index.html');
});
//include static files
app.use(express.static(__dirname + '/public'));
server = server.listen(3000);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (s) {
socket = s
socket.emit('c1', { hello: 'world' });
socket.on('test', function (data) {
socket.emit('c1', { hello: 'world' });
console.log('test');console.log(data);
});
});
And here is client code
var narad = {};
narad.url = 'http://192.168.0.46:3000';
narad.lisentingChannels = {}
var socket = io.connect(narad.url);
function Channel(channelName) {
this.channelName = channelName; //serviceObject is the object of
this.events = {};
};
Channel.prototype.bind = function (event, callback) {
this.events[event] = callback;
};
narad.subscribe = function (channelName) {
var channel = new Channel(channelName)
this.lisentingChannels[channelName] = channel;
socket.on(channelName, this.callbackBuilder(channel))
return this.lisentingChannels[channelName];
}
narad.callbackBuilder = function (channel) {
return function (data) {
var callback = channel.events[data["event"]];
callback(data.data);
}
}
You can use the emit method on both the client and the server websocket connections, taken from Socket.io:
var socket = io.connect(narad.url);
socket.emit('publish', 'message');
Then on your server you listen for the message:
socket.on('publish', function (data) {
// Emit the published message to the subscribers
socket.emit('subscribers', data);
console.log(data);
});
This way you are using the bi-directional communication of websockets without having to use some POST api.