I'm working on a chat application with Node.js and Socket.io, and on disconnect, the remaining user receives an alert saying that their partner disconnected.
The problem is that every once in a while, Socket.io automatically disconnects then reconnects. My chat application is triggering the alert, even though the partner hasn't really disconnected.
Code:
var clients = {};
var soloClients = [];
io.sockets.on('connection', function (socket) {
//Session start
socket.on('sessionStart', function () {
clients[socket.id] = socket;
soloClients.push(socket.id);
var searchClients = function(){
if(soloClients.length > 1){
var rand = Math.floor(Math.random() * soloClients.length);
if(soloClients[rand] && soloClients[rand] != socket.id){
if(clients[soloClients[rand]]){
var you = clients[socket.id];
var partner = clients[soloClients[rand]]
clients[partner.id]['partner'] = you.id;
clients[you.id]['partner'] = partner.id;
soloClients.splice(soloClients.indexOf(you.id), 1);
soloClients.splice(soloClients.indexOf(partner.id), 1);
partner.emit('partnerConnect', null);
socket.emit('partnerConnect', null);
}
else{
soloClients.splice(rand, 1);
searchClients;
}
}
else{
searchClients();
}
}
};
searchClients();
});
//On disconnect
socket.on('disconnect', function(){
soloClients.splice(soloClients.indexOf(socket.id), 1);
if(clients[socket.id]){
if(clients[clients[socket.id]['partner']]){
clients[clients[socket.id]['partner']].emit('partnerDisconnect', null);
}
delete clients[socket.id];
}
});
});
I was wondering if there is any way to solve this.
Thanks!
Maybe you should try to find out the real reason why the client gets disconnected? I.e. is it a bad connection, or firewall issues, or something else?
Also, you should check the error you get from the disconnect, if it's a clean disconnect (no error) you do the normal notification, but if it's an error you maybe want to handle it differently.
It turns out that this behavior isn't supposed to happen, it was just a bug in that version. This bug has been fixed in the latest version.
Related
Using socket.io 1.4.5.
It seems no matter what I do, I cannot prevent socket.io from firing the reconnection event or destroying the client socket when there are interruptions with the internet connection.
On the client side, I have:
reconnectionDelay: 99999999,
timeout: 99999999999,
reconnection: false,
And yet, if the internet disconnects, the socket will become undefined (after 20 seconds or so) and the reconnection event fires when the internet goes back on.
My ultimate goal is to use the same exact socket on the server and on the client (regardless of how long it's been since they've communicated) unless that socket is explicitly disconnected on the server. I cannot have the socket reconnecting at will, because I store data on the socket and use the socket.id extensively in my application. IF the socket and socket id were to suddenly change, the application breaks.
I think You've to pass some unique variable on handshake and keep data by that variable.
Regeneration of socket.id is normal behavior of socket.io.
In my practice I'm doing initial request to server to create slot variable and keep it slots collection in mongodb and after I'm creating connection by defining that slot variable as handshake.
or let's just simplify answer to Your question and use: https://www.npmjs.com/package/express-socket.io-session
also a little "hack" use namespaces logic: https://socket.io/docs/rooms-and-namespaces/#custom-namespaces
BONUS:
here is quick solution:
clientside:
function genUUID() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
var namespace = localStorage.getItem("namespace");
if(!namespace) {
namespace = genUUID();
localStorage.setItem("namespace", namespace);
}
var connect = function (ns) {
return io.connect(ns, {
query: 'ns='+ns // this is handshake variable `ns`
});
}
var socket = connect('/'+namespace);
backend:
const
url = require('url'),
sharedData = {};
io.sockets.on('connection', (socket) => {
const handshake = url.parse(socket.handshake.url, true);
const ns = handshake.query ? handshake.query.ns : 'default';
console.log('GOT CONNECTION TO NS: '+ns);
io.of(ns).on('connection', (socket) => {
if(!sharedData[ns]) sharedData[ns] = {};
// put socket code here
socket.on('some-event', (data) => {
sharedData[ns]['some-event'] = data.someData;
});
});
});
My server is running NodeJS and uses the amqplib api to request data from another application. The NodeJS server is receiving the information successfully but there's a noticable delay and I'm trying to determine whether I am doing this in the most efficient manner. Specifically I'm concerned with the way that I open and close connections.
Project Layout
I have two controller files that handle receiving and requesting the data, request.img.server.controller.js and receive.img.server.controller.js. Finally the routes handle the controller methods when a button on the front end is pushed, oct.server.routes.js.
request.img.server.controller.js
'use strict';
var amqp = require('amqplib/callback_api');
var connReady = false;
var conn, ch;
amqp.connect('amqp://localhost:5672', function(err, connection) {
conn = connection;
connReady = true;
conn.createChannel(function(err, channel) {
ch = channel;
});
});
exports.sendRequest = function(message) {
console.log('sending request');
if(connReady) {
var ex = '';
var key = 'utils';
ch.publish(ex, key, new Buffer(message));
console.log(" [x] Sent %s: '%s'", key, message);
}
};
receive.img.server.controller.js
var amqp = require('amqplib/callback_api');
var fs = require('fs');
var wstream = fs.createWriteStream('C:\\Users\\yako\\desktop\\binarytest.txt');
var image, rows, cols;
exports.getResponse = function(resCallback) {
amqp.connect('amqp://localhost:5672', function(err, conn) {
conn.createChannel(function(err, ch) {
var ex = '';
ch.assertQueue('server', {}, function(err, q) {
console.log('waiting for images');
var d = new Date();
var n = d.getTime();
ch.consume(q.queue, function(msg) {
console.log(" [x] %s: '%s'", msg.fields.routingKey, msg.content.toJSON());
rows = msg.content.readInt16LE(0);
cols = msg.content.readInt16LE(2);
console.log("rows = %s", msg.content.readInt16LE(0));
console.log("cols = %s", msg.content.readInt16LE(2));
image = msg.content;
var currMax = 0;
for (var i = 4; i < image.length; i+=2) {
if (image.readInt16LE(i) > currMax) {
currMax = image.readInt16LE(i);
}
wstream.write(image.readInt16LE(i) + ',');
}
console.log('done writing max is', currMax);
//console.log(image);
resCallback(rows, cols, image);
}, {
noAck: true
});
});
});
});
};
oct.server.routes.js
'use strict';
module.exports = function(app) {
var request_img = require('../../app/controllers/image-tools/request.img.server.controller.js');
var receive_img = require('../../app/controllers/image-tools/receive.img.server.controller.js');
// oct routes
app.get('/load_slice', function(req, res) {
console.log('load slice hit');
receive_img.getResponse(function (rows, cols, image) {
res.end(image);
});
request_img.sendRequest('123:C:\\Users\\yako\\Documents\\Developer\\medicaldiag\\test_files\\RUS-01-035-09M-21.oct');
});
};
The way you're opening connections is bad, and is at least part of the performance problem.
Connections are expensive to open. They open a new TCP/IP connection on a TCP/IP port between the client and rabbitmq server. This takes time, and uses up a limited resource on both the client and server.
Because of this, a single connection to RabbitMQ should be created and used within each of your node.js processes. This one connection should be shared by all of the code in that process.
Whenever you need to do something with RabbitMQ, open a new channel on the shared connection and do your work. Channels are cheap and are meant to be opened and closed as needed, within a connection.
More specifically in your code, the receive.img.server.controller.js file is the major problem. This opens a new connection to RabbitMQ every time you call the getResponse method.
If you have 10 users hitting the site, you'll have 10 open RabbitMQ connections when 1 would be sufficient. If you have thousands of users hitting the site, you'll have thousands of open RabbitMQ connections when 1 would be sufficient. You also run the risk of exhausting your available TCP/IP connections on the RabbitMQ server or client.
Your receive.img.server.controller.js should look more like your request.img.server.controller.js - one connection open, and re-used all the time.
Also, FWIW - I recommend using the wascally library for RabbitMQ w/ node.js. This library sits on top of amqplib, but makes things significantly easier. It will manage your one connection for you, and make it easier for you to send and receive messages.
I also have some training material available for RabbitMQ and node.js that covers the basics of amqplib and then moves in to using wascally for real application development.
I have problems with simple push on ffos. Here is my code:
var endpoint;
$(document).ready(function() {
registerPush();
navigator.mozSetMessageHandler("push", function(message) {
if(message.pushEndpoint == endpoint) {
console.log("push notification: "+message.version);
}
});
navigator.mozSetMessageHandler("push-register", function() {
registerPush();
});
});
function registerPush() {
var req = navigator.push.register();
req.onsuccess = function(e) {
endpoint = e.target.result;
console.log(endpoint);
}
}
It is working ok when i start the app, but after few minutes if i try to send a push to the endpoint, nothing happens. But when i call the registerPush(); function, everithing works again for few minutes.
It is said that there may be a bug. Some routers may are filtering the push notifications, so that may be the problem. I think It may be bypassed using a 3G connection.
Is there a way to emit a message via socket.io while excluding some socket id?
I know about the existence of rooms, but that's a no-no for me.
If what I'm trying is impossible, what should I do between those two things:
a) Iterate over all users that I want to send a message (in fact, all of them except 1) and do a emit for each socket
or
b) Just emit the message to everyone and do something hacky on the client side to "ignore" the message.
EDIT: I can't do a broadcast because the message is generated from the server side (so there is no client interaction).
Rooms - are not the way to accomplish of what you are trying to do, as they are meant to be used only to limit groups of people, but not specific.
What you are looking for is simple collection of users and ability to set some sort of filter on them.
Iteration through list of sockets - is not that critical, as when you broadcast - it does iteration anyway.
So here is small class that keeps sockets, removes them when needed (disconnect) and sends message to all with optional exception of single socket.
function Sockets() {
this.list = [ ];
}
Sockets.prototype.add = function(socket) {
this.list.push(socket);
var self = this;
socket.on('disconnect', function() {
self.remove(socket);
});
}
Sockets.prototype.remove = function(socket) {
var i = this.list.indexOf(socket);
if (i != -1) {
this.list.splice(i, 1);
}
}
Sockets.prototype.emit = function(name, data, except) {
var i = this.list.length;
while(i--) {
if (this.list[i] != except) {
this.list[i].emit(name, data)
}
}
}
Here is example of usage, user sends 'post' message with some data, and server just sends it to all in collection except of original sender:
var collection = new Sockets();
io.on('connection', function(socket) {
collection.add(socket);
socket.on('post', function(data) {
collection.emit('post', data, socket);
});
});
In fact it can be used as rooms as well.
the following code:
req.form.on('progress', function(bytesReceived, bytesExpected){
var percent = (bytesReceived / bytesExpected * 100) | 0;
// progressEvent.download(percent);
io.sockets.on('connection', function (socket) {
socket.emit('progress', { percent: percent});
client = socket;
});
});
written on an http post handler (express.js) sends socket messages to the client js, but it obviously creates a huge amount of listeners, in fact it warns me saying:
"node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit."
on the other hand this code:
io.sockets.on('connection', function (socket) {
progressEvent.on('progress', function(percentage) {
console.log(percentage);
socket.emit('progress', { percent: percentage});
});
});
Doesn't send any message back to the client, the ProgressEvent is:
var util = require('util'),
events = require('events');
function ProgressEvent() {
if(false === (this instanceof ProgressEvent)) {
return new ProgressEvent();
}
events.EventEmitter.call(this);
}
util.inherits(ProgressEvent, events.EventEmitter);
ProgressEvent.prototype.download = function(percentage) {
var self = this;
self.emit('progress', percentage);
}
exports.ProgressEvent = ProgressEvent;
I've been a good day on this strange problem I can't really see why socket.io doesn't send the socket message to the client.
the whole project is here: https://github.com/aterreno/superuploader
Thanks for your attention & help
You shouldn't listen to socket.io connections inside of the progress event. It looks like you're trying to get socket.io to connect when a user uploads a file, but that will not do that. Instead it will listen for a new connection each time the progress event fires on the upload, which I'm guessing is pretty often and it's why you're getting the warning about too many listeners.
What you want to do instead is on the client side, when you initialize an upload, tell the server through socket.io. Then the server links up that socket.io client with their upload through their session, http://www.danielbaulig.de/socket-ioexpress/
Something like this should do it
io.sockets.on('connection', function(socket) {
var session = socket.handshake.session;
socket.on('initUpload', function() {
session.socket = socket;
});
socket.on('disconnect', function() {
session.socket = null;
});
});
And then in your route
req.form.on('progress', function(bytesReceived, bytesExpected){
var percent = (bytesReceived / bytesExpected * 100) | 0;
if (req.session.socket) {
socket.emit('progress', percent);
}
});
This only works with one upload per session, but you get the idea.