简体   繁体   English

Socket.io无法将数据发送到客户端的独特房间

[英]Socket.io unable to emit data to client's unique room

I am using Node.js to create a media upload microservice. 我正在使用Node.js来创建媒体上传微服务。 This service works by taking in the binary data of the upload to a buffer, and then using the S3 npm package to upload to an S3 bucket. 此服务的工作原理是将上传的二进制数据输入缓冲区,然后使用S3 npm包上传到S3存储桶。 I am trying to use the eventEmitter present in that package which shows the amount of data uploaded to S3, and send that back to the client which is doing the uploading (so that they can see upload progress). 我正在尝试使用该包中的eventEmitter,它显示上传到S3的数据量,并将其发送回正在进行上载的客户端(以便他们可以看到上传进度)。 I am using socket.io for this sending of progress data back to the client. 我正在使用socket.io将进度数据发送回客户端。

The problem I am having is that the .emit event in socket.io will send the upload progress data to all connected clients, not just the client which initiated the upload. 我遇到的问题是socket.io中的.emit事件会将上传进度数据发送到所有连接的客户端,而不仅仅是发起上传的客户端。 As I understand it, a socket connects to a default room on 'connection', which is mirrored by the 'id' on the client side. 据我所知,套接字连接到'connection'上的默认房间,该房间由客户端的'id'镜像。 According to the official docs, using socket.to(id).emit() should send the data scoped only to that client, but this is not working for me. 根据官方文档,使用socket.to(id).emit()应该只将作用域的数据发送到该客户端,但这对我不起作用。

UPDATED Example code: 更新示例代码:

server.js: 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: 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: 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 the socket.io-parser taking in this information, but it isn't emitting it to the client: 当使用DEBUG = * node myapp.js时,我看到socket.io-parser接收了这些信息,但它没有将它发送给客户端:

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 remove the .to portion of this code, it sends the data to the client (albeit to all clients, which will not help at all): 但是,如果我删除此代码的.to部分,它会将数据发送到客户端(尽管对所有客户端都没有帮助):

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

DEBUG=* node myapp.js: 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 a different way to emit events from the server to only specific clients that I am missing? 是否有不同的方法将事件从服务器发送到我缺少的特定客户端?

The second example of code you posted should work and if it does not, you should post more code. 您发布的第二个代码示例应该可以使用,如果没有,您应该发布更多代码。

As I understand it, a socket connects to a default room on 'connection', which is mirrored by the 'id' on the client side. 据我所知,套接字连接到'connection'上的默认房间,该房间由客户端的'id'镜像。 According to the official docs, using socket.to(id).emit() should send the data scoped only to that client, but this is not working for me. 根据官方文档,使用socket.to(id).emit()应该只将作用域的数据发送到该客户端,但这对我不起作用。

Socket.io is pretty much easier than that. Socket.io比这简单得多。 The code below will send a 'hello' message to each client when they connect: 以下代码将在每个客户端连接时向其发送“hello”消息:

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

Everytime a new client connects to the socket.io server, it will run the specified callback using that particular socket as a parameter. 每当新客户端连接到socket.io服务器时,它将使用该特定套接字作为参数运行指定的回调。 socket.id is just an unique code to identify that socket but you don't really need that variable for anything, the code above shows you how to send a message through a particular socket . socket.id只是一个识别该套接字的唯一代码,但你并不真正需要该变量,上面的代码向您展示了如何通过特定socket发送消息。

Socket.io also provides you some functions to create namespaces/rooms so you can group connections under some identifier (room name) and be able to broadcast messages to all of them: Socket.io还为您提供了一些创建命名空间/房间的功能,因此您可以在某个标识符(房间名称)下对连接进行分组,并能够向所有这些标识符广播消息:

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'
    });
...

Now you should understand socket.join(socket.id) just does not make sense because no socket will be sharing socket id. 现在您应该了解socket.join(socket.id)只是没有意义,因为没有套接字将共享套接字ID。

Edit to answer the question with the new code: 编辑以使用新代码回答问题:

You have two problems here, first: 你有两个问题,第一个:

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

Note in the code above that everything inside io.sockets.on('connection',function (socket) { will be run each time a clients connect to the server. You are overwriting the function to point it to the socket of the latest user. 请注意,在上面的代码中,每次客户端连接到服务器时都会运行io.sockets.on('connection',function (socket) {内部的所有内容。您将覆盖该函数以将其指向最新用户的套接字。

The other problem is that you are not linking sockets and s3 operations. 另一个问题是您没有链接套接字和s3操作。 Here is a solution merging socket.js and s3upload.js in the same file. 这是在同一文件中合并socket.jss3upload.js的解决方案。 If you really need to keep them separated you will need to find a different way to link the socket connection to the s3 operation: 如果你真的需要将它们分开,你需要找到一种不同的方法来将套接字连接链接到s3操作:

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);
    });
};

According to the documentation all the users join the default room identified by the socket id, so no need for you to join in on connection. 根据文档,所有用户都加入了套接字ID标识的默认空间 ,因此您无需加入连接。 Still according to that, if you want to emit to a room in a namespace from a specific socket you should use socket.broadcast.to(room).emit('my message', msg) , given that you want to broadcast the message to all the clients connected to that specific room. 仍然根据这一点,如果你想从特定套接字发送到命名空间中的房间你应该使用socket.broadcast.to(room).emit('my message', msg) ,因为你想要广播消息连接到该特定房间的所有客户。

All new connections are automatically joined to room having the name that is equal to their socket.id. 所有新连接都自动连接到名称与其socket.id相同的房间。 You can use it to send messages to specific user, but you have to know socket.id associated with connection initialized by this user. 您可以使用它向特定用户发送消息,但您必须知道与此用户初始化的连接关联的socket.id You have to decide how you will manage this association (via databases, or in memory by having an array for it), but once you have it, just send progress percentage via: 您必须决定如何管理这种关联(通过数据库,或通过为其创建数组在内存中),但是一旦拥有它,只需通过以下方式发送进度百分比:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM