2015-11-18 3 views
6

Я использую Node.js для создания микросервиса для загрузки мультимедиа. Эта служба работает, беря двоичные данные загрузки в буфер, а затем используя пакет S3 npm для загрузки в ведро S3. Я пытаюсь использовать eventEmitter, присутствующий в этом пакете, который показывает объем данных, загружаемых на S3, и посылает их обратно клиенту, который выполняет загрузку (чтобы они могли видеть прогресс загрузки). Я использую socket.io для отправки данных прогресса клиенту.Socket.io не может передавать данные в уникальную комнату клиента

Проблема, с которой я столкнулась, заключается в том, что событие .emit в socket.io отправит данные о ходе загрузки для всех подключенных клиентов, а не только для клиента, который инициировал загрузку. Как я понимаю, сокет соединяется с комнатой по умолчанию на «соединении», которая зеркально отражается на «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 = * узел myapp.js, я вижу socket.io-pa rser принимая в этой информации, но она не излучающие его клиенту:

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 

Что я здесь делаю неправильно? Есть ли другой способ испускать события с сервера только конкретным клиентам, которых мне не хватает?

ответ

3

Второй пример кода, который вы опубликовали, должен работать, а если нет, вы должны разместить больше кода.

Как я понимаю, разъем подключается к комнате по умолчанию на «соединение» , которая отражается в «ид» на стороне клиента. Согласно официальным документам, использование socket.to (id) .emit() должно отправлять данные, привязанные только к этому клиенту, но это не работает для меня.

Socket.io намного проще, чем это. Приведенный ниже код будет посылать «привет» сообщение каждому клиенту, когда они подключаются:

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

Everytime новый клиент подключается к розетке.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) просто не имеет смысла, потому что ни один сокет не будет использовать идентификатор сокета.

Редактировать, чтобы ответить на этот вопрос с новым кодом:

У вас есть две проблемы здесь, первое:

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.js и s3upload.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); 
    }); 
}; 
+0

Я редактировал свой вопрос, чтобы показать больше базовой архитектуры кода. Я не могу просто испускать соединение, поскольку передаю информацию с другого маршрута в сокет. –

+0

Я отредактировал свой ответ в соответствии с вашим кодом, я объединил 'socket.js' и' s3upload.js' в том же файле, чтобы упростить его. Если вам действительно нужно их разделить, я бы рекомендовал вам переключить зависимость, используя socket.js, импортирующий S3, а не наоборот. Вам нужно будет вызывать функции на основе событий сокета, поэтому ему необходимо будет получить доступ к функциям 's3upload.js'. –

+0

Я попытаюсь перестроить свое приложение с вашими предложениями, спасибо за помощь. Связанный с вами вопрос основывается на вашем предположении о перемещении моего кода: верно ли, что при использовании сокета приложения действительно должны быть созданы вокруг socket.io, чтобы вся логика передавалась через обработчик сокета? Если это правда, не могли ли сделать выделенные маршруты невозможными для записи при использовании сокета? –

1

Согласно документации все пользователи присоединяются к default room, идентифицированным идентификатором сокета, поэтому вам не нужно подключаться к соединению. Тем не менее, если вы хотите, чтобы вы выбрали комнату в пространстве имен из определенного сокета, вы должны использовать socket.broadcast.to(room).emit('my message', msg), учитывая, что вы хотите передать это сообщение всем клиентам, подключенным к этой конкретной комнате.

+0

обновляя свой код, который дает мне такая же проблема. Он запускает синтаксический анализатор, но не движок и сценарий. Я запускаю socket.io 1.3.6, поэтому эти функции определенно определены. Дайте мне знать, если бы увидели больше моего кода, или если есть способ дальнейшего отладки этого, что я не пытаюсь. –

1

Все новые соединения автоматически присоединены к комнате, имеющее имя, которое равно их гнезду .Я бы. Вы можете использовать его для отправки сообщений конкретному пользователю, но вы должны знать socket.id, связанный с соединением, инициализированным этим пользователем. Вы должны решить, как вы будете управлять этой ассоциацией (через базу данных, или в памяти, имея массив для него), но как только вы имеете его, просто отправить процент прогресса через:

socket.broadcast.to(user_socket_id).emit("progress", number_or_percent); 
Смежные вопросы