【问题标题】:Socket.io unable to emit data to client's unique roomSocket.io 无法向客户端的唯一房间发送数据
【发布时间】:2015-11-18 21:12:05
【问题描述】:

我正在使用 Node.js 创建媒体上传微服务。该服务的工作原理是将上传的二进制数据接收到缓冲区,然后使用 S3 npm 包上传到 S3 存储桶。我正在尝试使用该包中存在的 eventEmitter 来显示上传到 S3 的数据量,并将其发送回正在上传的客户端(以便他们可以看到上传进度)。我正在使用 socket.io 将进度数据发送回客户端。

我遇到的问题是 socket.io 中的 .emit 事件会将上传进度数据发送到所有连接的客户端,而不仅仅是启动上传的客户端。据我了解,套接字连接到“连接”上的默认房间,该房间由客户端的“id”镜像。根据官方文档,使用 socket.to(id).emit() 应该只将范围内的数据发送到该客户端,但这对我不起作用。

更新示例代码:

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

在使用 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

但是,如果我删除此代码的 .to 部分,它会将数据发送给客户端(尽管发送给所有客户端,这根本无济于事):

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

DEBUG=* 节点 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

我在这里做错了什么?是否有其他方法可以将事件从服务器仅发送到我缺少的特定客户端?

【问题讨论】:

    标签: javascript node.js amazon-s3 socket.io


    【解决方案1】:

    您发布的第二个代码示例应该可以工作,如果不行,您应该发布更多代码。

    据我了解,套接字连接到默认房间 'connection',由客户端的 'id' 镜像。 根据官方文档,使用 socket.to(id).emit() 应该发送 数据范围仅适用于该客户,但这对我不起作用。

    Socket.io 比这容易得多。下面的代码将在每个客户端连接时向它们发送一条“hello”消息:

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

    每次新客户端连接到 socket.io 服务器时,它都会使用该特定套接字作为参数运行指定的回调。 socket.id 只是标识该套接字的唯一代码,但您实际上并不需要该变量来做任何事情,上面的代码向您展示了如何通过特定的 socket 发送消息。

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

    现在你应该明白socket.join(socket.id) 是没有意义的,因为没有套接字会共享套接字 ID。

    编辑以使用新代码回答问题:

    这里有两个问题,首先:

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

    请注意,在上面的代码中,io.sockets.on('connection',function (socket) { 中的所有内容都将在每次客户端连接到服务器时运行。您正在覆盖该函数以将其指向最新用户的套接字。

    另一个问题是您没有链接套接字和 s3 操作。这是将socket.jss3upload.js 合并到同一个文件中的解决方案。如果您确实需要将它们分开,您将需要找到一种不同的方式将套接字连接链接到 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);
        });
    };
    

    【讨论】:

    • 我已经编辑了我的问题以显示更多的底层代码架构。我不能简单地在连接时发出,因为我正在将信息从另一条路由传递到套接字。
    • 我已根据您的代码编辑了我的答案,我已将socket.jss3upload.js 合并到同一个文件中以使其更容易。如果您确实需要将它们分开,我建议您切换具有 socket.js 导入 S3 的依赖项,而不是相反。您需要根据套接字事件调用函数,因此它需要访问 s3upload.js 函数。
    • 我会尝试根据您的建议重新构建我的应用程序,感谢您的帮助。基于您建议移动我的代码的相关问题:使用套接字时,应用程序是否真的应该围绕 socket.io 设计,以便所有逻辑都通过套接字处理程序传递?如果这是真的,那不会在使用套接字时无法编写专用路由吗?
    • 我已经完成了一些 socket.io 应用程序并尝试了与您在上面所做的相同的操作,但没有取得多大成功。我能够将它下推到依赖层次结构并在上层配置一些套接字事件,但最后我意识到从设计的角度来看它变得更加复杂和冗余。 HTTP 服务器在上面和 socket.io 相同的模块上很好(根据我的经验),因为它是一项服务,但如果你要使用由套接字事件触发的后端函数,你最好将它们放在下面或同一个模块中。
    • 再次感谢您的解释。 socket.io 中是否有某些内容导致套接字方法仅适用于调用它的模块?这似乎是一个常见的用例,人们希望使用套接字逐步增强节点应用程序,而不是基于 socket.io 构建整个后端逻辑。我知道 socket-emitter 模块,但这也需要一些依赖项,并且由于 socket 在定义时位于整个节点对象中,所以我想不出为什么这些方法不能是全局的。
    【解决方案2】:

    根据文档,所有用户都加入了由套接字 id 标识的default room,因此您无需在连接时加入。尽管如此,如果您想从特定套接字向命名空间中的房间发送消息,则应该使用socket.broadcast.to(room).emit('my message', msg),因为您希望将消息广播到连接到该特定房间的所有客户端。

    【讨论】:

    • 用它更新我的代码给了我同样的问题。它正在运行解析器,但不是引擎或写入流。我正在运行socket.io 1.3.6,所以这些函数是明确定义的。让我知道查看更多我的代码是否会有所帮助,或者是否有办法进一步调试我没有尝试的。
    【解决方案3】:

    所有新连接都会自动加入到名称与其 socket.id 相同的房间。您可以使用它向特定用户发送消息,但您必须知道与此用户初始化的连接关联的socket.id。您必须决定如何管理此关联(通过数据库,或在内存中通过为其设置一个数组),但是一旦您拥有它,只需通过以下方式发送进度百分比:

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

    【讨论】:

      猜你喜欢
      • 2015-08-06
      • 2018-10-19
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      • 2014-08-17
      • 2018-11-06
      • 1970-01-01
      相关资源
      最近更新 更多