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

时间:2015-11-18 21:12:06

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

我正在使用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 = * 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

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

3 个答案:

答案 0 :(得分:3)

您发布的第二个代码示例应该可以使用,如果没有,您应该发布更多代码。

  

据我所知,套接字连接到默认房间   '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);
    });
};

答案 1 :(得分:1)

根据文档,所有用户都加入了套接字ID所标识的default room,因此您无需加入连接。仍然根据这一点,如果你想从特定套接字发送到命名空间中的房间,你应该使用socket.broadcast.to(room).emit('my message', msg),因为你想要将消息广播到连接到那个特定房间的所有客户端。

答案 2 :(得分:1)

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

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