Why does Socket.io emit empty events at random intervals? - node.js

I have an application that runs on Nodejs and Socket.io
The events 2, 3 seen in the image are empty events that socket.io server keeps emitting at random intervals. What's more annoying is that the time delay between the empty events is crazy, as seen in the picture there was a 19 seconds time delay between 2 and 3. And due to this, the message:delete event that was emitted by socket.io client which was supposed to immediately emit message:deleted, got delayed by 19 seconds!!
What's worse is that while these empty events are emitted, all other socket events originated from socket client are not emitted and they are stuck in limbo until the empty events are out of the way.
I suspect these empty events are socket ping and pong events. Does anyone have any idea why this happens? And how we could prevent this? Or maybe even set a priority for client socket.emit so that regardless of these empty events, the client emitted socket event takes precedence and is fired immediately?
Edit #1
Despite adding { transports : ['websocket'] } the delay was still well over 17 seconds as you can see in this image
Edit #2 - The socket.emit code
The client is made up of a lot of jQuery + vanilla JS + Angular 1.7. The client socket is a factory wrapper
app.factory('socket',['$rootScope', function ($rootScope) {
var socket = io.connect('',{
path: '/socket.io',
transports: ['websocket']
});
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);
}
});
})
},
disconnect: function(close){
socket.disconnect(close);
},
removeAllListeners: function (eventName, callback) {
socket.removeAllListeners(eventName, function() {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
}
};
}])
And on the controller the `socket.emit' code is as follows
$scope.deleteMessage = function (message) {
socket.emit('message:delete', {
id: message
});
};
I use express + Nodejs so here's the code for initializing the server socket
Server = require('socket.io'),
io = new Server({
path: `/socket.io`,
transports: ['websocket']
});
And the server side socket code listens to the client emit as follows
io.on("connection", async function (socket) {
socket.on("message:delete", function (payload) {
if (props.connectedUsers[socket.userId].info.isAdmin) {
console.log("Received Message Deletion by",
props.connectedUsers[socket.userId].info.name);
if (payload.id == "SYS_MOTD") return;
await Message.remove({
_id: new db.Types.ObjectId(payload.id)
}).exec(function (err, message) {
if (!err) {
console.log("Emit message:deleted");
io.sockets.emit("message:deleted", {
id: payload.id
});
}
});
}
});
});

I think it's normal, according to the Socket.IO documentation:
By default, a long-polling connection is established first, then
upgraded to “better” transports (like WebSocket).
What you can do is to set the connection to use websocket only, it should stop the long-polling and you will receive the messages immediately:
socket = io.connect({transports: ['websocket']});
Update
No need to set the path to /socket.io, it's the default value.
On Client side you can also use:
const socket = io([URL], {
transports: ['websocket']
});
I've noticed you are using async/await with a callback, I would either use async/await or a callback on your server side code, for example:
try {
await Message.remove({
_id: new db.Types.ObjectId(payload.id)
})
console.log("Emit message:deleted");
io.sockets.emit("message:deleted", {
id: payload.id
});
} catch(ex) {
console.log(ex.message);
}

Related

io.to(socket.id).emit() not working on cross domain

how to emit the event with using particular_room [io.to(socket.id).emit('sendData') ]at cross domain
I've tried this,
server side
io.emit('sendData', data);
clientside
var socket = io('https://localhost:3000/', { transports: ['websocket'] });
socket.on('sendData', function (data) {
console.log(data);
})
above syntax are perfectly working on cross domain
but I want to emit with particular room at cross domain
io.to(socket.id).emit('sendData', data)
io.broadcast.to(socketid).emit('message', 'for your eyes only'); //sending to individual socketid
Reference link -> Cross-domain connection in Socket.IO
Kindly help to solve this!
I Understood your code, you just emit the initial connection on the socket.io server
io.on("connection", function(socket){
socket.emit('sendsocketid', data);
})
Cross-domain clientside
var socket = io('https://localhost:3000/', { transports: ['websocket'] });
var obj= [];
socket.on('sendsocketid', function (data) {
console.log(data);
// Now You Can Customize the or add the client Socket .id
var NewSocketid = {
id: socket.id
}
Obj.push(data, NewSocketid)
socket.emit("emitnewSocketId", Obj)
})
socket.on("emitnewSocketId", function(data){
consoleo.log(Obj[1].NewSocketid)
//Now You can emit the new socket id
io.to(Obj[1].NewSocketid).emit('sendData', data)
})

socket io and mqtt nodejs duplicate entry

I am using mqttjs and socketio on my nodejs backend.
I am using angular as my frontend framework.
On my frontend there are 3 routes.
All requires socket connection for real time data.
So on ngOnInit i run client side socket io connection code and on ngOnDestroy I will run socket disconnect as well.
And in my server side code (index.js) there are mainly 3 actions that is happening.
const io = require('socket.io')(server)
mqtt.createConnection();
mqtt.mqttSubscriptions(io);
mqtt.mqttMessages(io);
These are the mqtt methods:
const createConnection = () => {
let options = {
protocol: 'mqtt',
clientId: process.env.MQTT_CLIENT_ID,
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
};
client = mqtt.connect(process.env.MQTT_HOST, options);
client.on('connect', function() {
winston.info('MQTT connected');
});
client.on('error', function(err) {
winston.error(err);
});
};
const mqttSubscriptions = io => {
winston.info(`Socket connected.`);
client.subscribe([TOPICS.DATA], function(error, granted) {
if (error) {
winston.error(error);
}
winston.info('Topics: ', granted);
});
};
const mqttMessages = io => {
io.sockets.on('connection', socket => {
winston.info(`Socket connected.`);
client.on('message', function(topic, message) {
let payload = JSON.parse(message.toString());
winston.info(topic);
winston.info(payload.id);
switch (topic) {
case TOPICS.DATA:
dataController.storeData(payload, io);
break;
default:
winston.error('Wrong topic');
break;
}
});
});
};
And on the datacontroller I am running
socket.emit()
My problem is everytime I navigate to a route and come back the dataController.storeData is called multiple times.
That is when I am at route A, and then navigate to route B and then back to A and then to C, the data is multiplied that many times of my route navigation. (In this case 4 times.)
I found that it is socket io and mqtt connection problem, but I don't know how to solve, since I am new to both of these.
Any help?

Node.js - Socket.io-client does not emit data

I'm trying to build a simple socket.io-client using nodejs, but I'm facing a trouble...
I'm connecting with the socket.io (server), but I can't emit any data. Follow bellow my simple code:
Client Side:
var socketIO = require('socket.io-client')('http://serverdns:3000');
socketIO.on("dashboard", (data) => {
console.log(data);
});
socketIO.on('connect', function(){
console.log("Connected with the translator service.");
socketIO.emit('dashboard', 'teste');
});
socketIO.on('disconnect', function(){
console.log("Disconnected from the translator service");
});
socketIO.on('error', function(err){
console.log(err);
});
Socket.io version: 2.1.1 (I've tried to use old versions but the same problem happens).
The connect event works, the log "Connected with the translator service." is generated, but emit does not work.
Server side:
var server = require('http').createServer();
var ioServer = require('socket.io')(server, { pingInterval: 2000, pingTimeout: 60000, cookie: false });
class SocketServer {
constructor() {
var self = this;
ioServer.on('connection', function (client) {
console.log('[SOCKETIO] AVAILABLE');
client.on('main', self.main);
client.on('disconnect', self.disconnect);
});
server.listen(3000);
}
getSocket(){
return ioServer;
}
main(data) {
console.log(data);
}
disconnect() {
console.log("[SOCKETIO] DISCONNECTED");
}
}
module.exports = new SocketServer();
Anyone can help me?
Are there anything I'm not seeing?
Thanks a lot.
Right now you are emitting to the event dashboard from client. But on the server side you have no code that is handling that event. You are currently logging the event main which does not match with what you're emitting. Try client.on('dashboard', self.dashboard). Make your own dashboard function.

Socket.io connected but not communicating

I have a very simple configuration in a node server with socket.io installed (a little bit more complex but essentially like this one):
var main = require('express')();
server = require('http').createServer(main);
io = require('socket.io')(server);
io.use(function(socket, next) {
console.log("middleware!");
next();
});
io.on('connection', function (socket) {
console.log('connected...');
socket.on('pong', function (data) {
console.log(data.message);
});
setTimeout(function() {
console.log("Saying hello");
socket.emit('ping', { message: 'Hello from server ' + Date.now() });
}, 1000);
});
server.listen(2080, function onCreateServerMain() {
console.log('Server main is listening on port 2080';
console.log('************************************************************');
});
In the client:
var socketIoScript,
loadSocketTimeout,
trialsToLoadSocketIo = 0,
APP_CFG = {baseUrl : "http://192.168.1.13:2080"};
function loadSocketIo(socketIoIp) {
socketIoScript = document.createElement('script');
socketIoScript.setAttribute('src', socketIoIp);
socketIoScript.setAttribute('onload', 'onSocketLoaded();');
document.head.appendChild(socketIoScript);
}
window.onSocketLoaded = function onSocketLoaded() {
if (typeof(io.connect) === 'function') {
var mSocket,
mIoSocket;
$timeout.cancel(loadSocketTimeout);
mIoSocket = new io.Manager(APP_CFG.baseUrl);
mIoSocket.connect(function(socket) {
console.log('Connected!!');
});
mIoSocket.on('error', function onSocketError(e) {
console.log('WebSocket Error ' + error);
});
mIoSocket.on('ping', function onPingReceived(e) {
console.log('Server emitted ping: ' + e.data);
mSocket.emit('pong', 'hi server!');
});
}
}
~(function onLoadSocketTimeout() {
var nextTimeout;
if (trialsToLoadSocketIo < 10) {
nextTimeout = 5000;
} else if (trialsToLoadSocketIo > 60) {
nextTimeout = 60000;
} else {
nextTimeout = 1000 * trialsToLoadSocketIo;
}
if (socketIoScript) {
document.head.removeChild(socketIoScript);
}
loadSocketIo(APP_CFG.baseUrl + '/socket.io/socket.io.js#' + trialsToLoadSocketIo);
loadSocketTimeout = $timeout(onLoadSocketTimeout, nextTimeout);
trialsToLoadSocketIo += 1;
})();
(I'm doing like this because it's mobile app so it may have not connection). I'm testing it with Brackets and Chrome. Server and client are in the same machine. In the app the script is loaded fine and it connects to the server as I can it see in node log (edit: and this is all what I get in the node console):
Server main is listening on port 2080
************************************************************
middleware!
connected...
Saying hello
Edit: in Chrome console I don't get any message, and any breakpoint stops at on listeners. If I stop node, the console for the Chrome immediately starts logging that it has been disconnected:
GET http://192.168.1.13:2080/socket.io/?EIO=3&transport=polling&t=1413066902601-6 net::ERR_CONNECTION_REFUSED
GET http://192.168.1.13:2080/socket.io/?EIO=3&transport=polling&t=1413066906606-7 net::ERR_CONNECTION_REFUSED
But I can't see any incoming message. In the app I don't receive any incoming message. Is there any reason why I could not communicate in this environment even if socket is successfully connected?
EDIT
No app is receiving events sent from the other side. Logs from node show this, logs from Chrome are empty.
EDIT
In Chrome app I don't receive console.log("Connected!");. But neither I receive ERR_CONNECTION_REFUSED errors: I don't receive anything.
EDIT
I managed to get console.log("Connected!"); in the app by changing Manager options:
mIoSocket = new io.Manager(APP_CFG.baseUrl, { autoConnect: false });
As it was auto connecting and the events were attached after connection was made, "Connected" was never reached. But I'm still not receiving any event in any app.
I had a similar issue were event callbacks on the server were not firing when emitting. My event names were ping and pong. As soon as I renamed these events everything worked.
I suspect the event names ping and pong are reserved by socket.io and so cannot be used.
Ok, so a few things :
First, var mSocket doesn't seem to be initialized, so it may be difficult for it to emit() anything (am I missing something?)
Second, when you do :
socket.on('pong', function (data) {
console.log(data.message);
});
the server expects to receive an object containing a message property, eg : data = {message:'hi server'} In your case, you send a string, so data is 'Hi server !' and your log will say 'undefined'. You should change this bit to :
socket.on('pong', function (data) {
console.log(data);
});
and you have a similar problem the other way around, you send an object : { message: 'Hello from server ' + Date.now() }, and are trying to log a data property which does not exist. Change this bit to :
console.log('Server emitted ping: ' + e.message);
And third , you have to listen for events on the socket, not the 'manager'
Client :
mIoSocket.connect(function(socket) {
console.log('Connected!!');
socket.emit('pong');
socket.on('error', function onSocketError(e) {
console.log('WebSocket Error ' + error);
});
socket.on('ping', function onPingReceived(e) {
console.log('Server emitted ping: ' + e.data);
socket.emit('pong', 'hi server!');
});
});
Server :
io.on('connection', function (socket) {
console.log('connected...');
socket.on('pong', function (data) {
console.log(data);
});
setTimeout(function() {
console.log("Saying hello");
socket.emit('ping', { message: 'Hello from server ' + Date.now() });
}, 1000);
});

Client can only emit once and "force new connection" duplicates the response.

my client side can only emit once and "force new connection" duplicates the response back to my client. here's my code so you can look it up.
server.js
var app = require('http').createServer()
, io = require('socket.io').listen(app);
app.listen(5000);
io.sockets.on('connection', function(socket) {
socket.on('sendSheet', function(data) {
io.sockets.emit('displayData', data);
});
socket.on('disconnect', function() {
io.sockets.emit('user disconnected');
});
});
client.js
var socket = io.connect('http://localhost:5000', {'force new connection': true});
socket.on('dispatchConnect', function (data) {
socket.emit('sendSheet', mergedForm);
});
socket.on('displayData', function (data) {
console.log(data);
});
Read about asynchronous functions in nodejs and try to understand "the node event loop".
Your code is blocking couse your functions are synchronous .
Since an event loop runs in a single thread, it only processes the next event when the callback finishes.
You should never use a blocking function inside a callback, since you’re blocking the event loop
and preventing other callbacks - probably belonging to other client connections - from being served.
Here is a async example:
var myAsyncFunction = function(someArg, callback) { // simulate some I/O was done
setTimeout(function() { // 1 second later, we are done with the I/O, call the callback
callback();
}, 1000)
}

Resources