Socket.io cannot transfer data to a unique client room

I use Node.js to create a microservice for downloading multimedia. This service works by taking the binary download data to the buffer, and then using the S3 npm package to load into the S3 bucket. I am trying to use the eventEmitter present in this package, which shows the amount of data uploaded to S3, and sends it back to the client that is loading (so that they can see the download progress). I use socket.io to send progress data to the client.

The problem I encountered is that the .emit event in socket.io will send download progress data to all connected clients, not just the client that initiated the download. As I understand it, the socket connects to the default room on the โ€œconnectionโ€, which mirrors the โ€œidโ€ on the client side. According to official docs, using socket.to (id) .emit () should send data that is bound only to this client, but this does not work for me.

UPDATED Code example:

server.js:

var http = require('http'), users = require('./data'), app = require('./app')(users); var server = http.createServer(app); server.listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); }); var io = require('./socket.js').listen(server); 

socket.js:

 var socketio = require('socket.io'); var socketConnection = exports = module.exports = {}; socketConnection.listen = function listen(app) { io = socketio.listen(app); exports.sockets = io.sockets; io.sockets.on('connection', function (socket) { socket.join(socket.id); socket.on('disconnect', function(){ console.log("device "+socket.id+" disconnected"); }); socketConnection.upload = function upload (data) { socket.to(socket.id).emit('progress', {progress:(data.progressAmount/data.progressTotal)*100}); }; }); return io; }; 

s3upload.js:

 var config = require('../config/aws.json'); var s3 = require('s3'); var path = require('path'); var fs = require('fs'); var Busboy = require('busboy'); var inspect = require('util').inspect; var io = require('../socket.js'); ... var S3Upload = exports = module.exports = {}; .... S3Upload.upload = function upload(params) { // start uploading to uploader var uploader = client.uploadFile(params); uploader.on('error', function(err) { console.error("There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection: ", err.stack); res.json({responseHTML: "<span>There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection. Please refresh and try again.</span>"}); throw new Error(err); }), uploader.on('progress', function() { io.upload(uploader); }), uploader.on('end', function(){ S3Upload.deleteFile(params.localFile); }); }; 

When using DEBUG = * node myapp.js, I see that socket.io-parser accepts this information but does not pass it to the client:

 socket.io-parser encoding packet {"type":2,"data":["progress",{"progress":95.79422221709825}],"nsp":"/"} +0ms socket.io-parser encoded {"type":2,"data":["progress",{"progress":95.79422221709825}],"nsp":"/"} as 2["progress",{"progress":95.79422221709825}] +0ms 

However, if I delete the .to part of this code, it will send data to the client (although to all clients, which will not help at all):

 io.sockets.on('connection', function(socket) { socket.join(socket.id); socket.emit('progress', {progress: (data.progressAmount/data.progressTotal)*100}); }); 

DEBUG = * node myapp.js:

 socket.io:client writing packet {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} +1ms socket.io-parser encoding packet {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} +1ms socket.io-parser encoded {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} as 2["progress",{"progress":99.93823786632886}] +0ms engine:socket sending packet "message" (2["progress",{"progress":99.93823786632886}]) +0ms engine:socket flushing buffer to transport +0ms engine:ws writing "42["progress",{"progress":99.84186540937002}]" +0ms engine:ws writing "42["progress",{"progress":99.93823786632886}]" +0ms 

What am I doing wrong here? Is there any other way to emit events from the server only to specific clients that I miss?

+6
source share
3 answers

The second code sample that you posted should work, and if not, you should post more code.

As I understand it, the socket connects to the room by default on the "connection", which mirrors the "id" on the client side. According to official docs, using socket.to (id) .emit () should send the data attached only to this client, but this does not work for me.

Socket.io is much simpler than that. The code below will send a "hello" message to each client when they connect:

 io.sockets.on('connection', function (socket) { socket.emit('hello'); }); 

Each time a new client connects to the socket.io server, it starts the specified callback using this specific socket as a parameter. socket.id is just a unique code to identify this socket, but you really don't need this variable, the code above shows you how to send a message through a specific socket .

Socket.io also provides you with some functions for creating namespaces / rooms so that you can group connections under a certain identifier (room name) and be able to send messages to all of them:

 io.sockets.on('connection', function (socket) { // This will be triggered after the client does socket.emit('join','myRoom') socket.on('join', function (room) { socket.join(room); // Now this socket will receive all the messages broadcast to 'myRoom' }); ... 

You should now understand that socket.join(socket.id) just doesn't make sense, because no socket will use the socket id.

Change the answer to the question with the new code:

You have two problems here:

  socketConnection.upload = function upload (data) { socket.to(socket.id).emit('progress', {progress:(data.progressAmount/data.progressTotal)*100}); }; 

Note the code above that everything inside io.sockets.on('connection',function (socket) { will be executed every time clients connect to the server. You rewrite the function to point it to the socket of the last user.

Another problem is that you are not binding sockets and s3 operations. Here is a solution combining socket.js and s3upload.js in the same file. If you really need to separate them, you will need to find another way to associate the socket connection with the s3 operation:

 var config = require('../config/aws.json'); var s3 = require('s3'); var path = require('path'); var fs = require('fs'); var Busboy = require('busboy'); var inspect = require('util').inspect; var io = require('socket.io'); var socketConnection = exports = module.exports = {}; var S3Upload = exports = module.exports = {}; io = socketio.listen(app); exports.sockets = io.sockets; io.sockets.on('connection', function (socket) { socket.on('disconnect', function(){ console.log("device "+socket.id+" disconnected"); }); socket.on('upload', function (data) { //The client will trigger the upload sending the data /* some code creating the bucket params using data */ S3Upload.upload(params,this); }); }); S3Upload.upload = function upload(params,socket) { // Here we pass the socket so we can answer him back // start uploading to uploader var uploader = client.uploadFile(params); uploader.on('error', function(err) { console.error("There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection: ", err.stack); res.json({responseHTML: "<span>There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection. Please refresh and try again.</span>"}); throw new Error(err); }), uploader.on('progress', function() { socket.emit('progress', {progress:(uploader.progressAmount/uploader.progressTotal)*100}); }), uploader.on('end', function(){ S3Upload.deleteFile(params.localFile); }); }; 
+3
source

According to the documentation, all users join the default number indicated by the socket ID, so you do not need to connect to the connection. However, according to this, if you want to get into a room in the namespace from a specific socket, you should use socket.broadcast.to(room).emit('my message', msg) , given that you want to send a message all customers connected to this particular room.

+1
source

All new connections are automatically connected to the room with a name equal to their socket.id. You can use it to send messages to a specific user, but you should know socket.id associated with the connection initiated by that user. You have to decide how you will manage this association (through databases or in memory, having an array for it), but as soon as you get it, just send the percentage of progress with:

 socket.broadcast.to( user_socket_id ).emit( "progress", number_or_percent ); 
+1
source

All Articles