Socket.io 无法向客户的专属房间发送数据
Socket.io unable to emit data to client's unique room
我正在使用 Node.js 创建媒体上传微服务。该服务的工作原理是将上传的二进制数据接收到缓冲区,然后使用 S3 npm 包上传到 S3 存储桶。我正在尝试使用该包中的 eventEmitter,它显示上传到 S3 的数据量,并将其发送回正在上传的客户端(以便他们可以看到上传进度)。我正在使用 socket.io 将进度数据发送回客户端。
我遇到的问题是 socket.io 中的 .emit 事件会将上传进度数据发送到所有连接的客户端,而不仅仅是启动上传的客户端。据我了解,一个套接字连接到 'connection' 上的默认房间,它由客户端的 '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-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
我在这里做错了什么?是否有不同的方式从服务器向我缺少的特定客户端发出事件?
根据文档,所有用户都加入了由套接字 ID 标识的 default room,因此您无需加入连接。仍然根据那个,如果你想从一个特定的套接字发送到命名空间中的一个房间,你应该使用 socket.broadcast.to(room).emit('my message', msg)
,假设你想将消息广播到连接到该特定房间的所有客户端。
您 post 编写的第二个代码示例应该可以工作,如果没有,您应该 post 更多代码。
As I understand it, a socket connects to a default room on
'connection', which is mirrored by the 'id' on the client side.
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.io 比这容易得多。下面的代码将在连接时向每个客户端发送 'hello' 消息:
io.sockets.on('connection', function (socket) {
socket.emit('hello');
});
每次新客户端连接到 socket.io 服务器时,它将 运行 使用该特定套接字作为参数的指定回调。 socket.id
只是识别该套接字的唯一代码,但您实际上并不需要该变量,上面的代码向您展示了如何通过特定的 socket
.
发送消息
Socket.io 还为您提供了一些创建 namespaces/rooms 的函数,因此您可以将连接分组在某个标识符(房间名称)下,并能够向所有这些连接广播消息:
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) {
中的所有内容都将是 运行。您正在覆盖函数以将其指向最新用户的套接字。
另一个问题是您没有linking 套接字和s3 操作。这是将 socket.js
和 s3upload.js
合并到同一个文件中的解决方案。如果您真的需要将它们分开,您将需要找到一种不同的方法来 link 套接字连接到 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.id 相同的房间。您可以使用它向特定用户发送消息,但您必须知道 socket.id
与该用户初始化的连接相关联。您必须决定如何管理此关联(通过数据库,或在内存中为其设置一个数组),但是一旦拥有它,只需通过以下方式发送进度百分比:
socket.broadcast.to( user_socket_id ).emit( "progress", number_or_percent );
我正在使用 Node.js 创建媒体上传微服务。该服务的工作原理是将上传的二进制数据接收到缓冲区,然后使用 S3 npm 包上传到 S3 存储桶。我正在尝试使用该包中的 eventEmitter,它显示上传到 S3 的数据量,并将其发送回正在上传的客户端(以便他们可以看到上传进度)。我正在使用 socket.io 将进度数据发送回客户端。
我遇到的问题是 socket.io 中的 .emit 事件会将上传进度数据发送到所有连接的客户端,而不仅仅是启动上传的客户端。据我了解,一个套接字连接到 'connection' 上的默认房间,它由客户端的 '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-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
我在这里做错了什么?是否有不同的方式从服务器向我缺少的特定客户端发出事件?
根据文档,所有用户都加入了由套接字 ID 标识的 default room,因此您无需加入连接。仍然根据那个,如果你想从一个特定的套接字发送到命名空间中的一个房间,你应该使用 socket.broadcast.to(room).emit('my message', msg)
,假设你想将消息广播到连接到该特定房间的所有客户端。
您 post 编写的第二个代码示例应该可以工作,如果没有,您应该 post 更多代码。
As I understand it, a socket connects to a default room on 'connection', which is mirrored by the 'id' on the client side. 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.io 比这容易得多。下面的代码将在连接时向每个客户端发送 'hello' 消息:
io.sockets.on('connection', function (socket) {
socket.emit('hello');
});
每次新客户端连接到 socket.io 服务器时,它将 运行 使用该特定套接字作为参数的指定回调。 socket.id
只是识别该套接字的唯一代码,但您实际上并不需要该变量,上面的代码向您展示了如何通过特定的 socket
.
Socket.io 还为您提供了一些创建 namespaces/rooms 的函数,因此您可以将连接分组在某个标识符(房间名称)下,并能够向所有这些连接广播消息:
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) {
中的所有内容都将是 运行。您正在覆盖函数以将其指向最新用户的套接字。
另一个问题是您没有linking 套接字和s3 操作。这是将 socket.js
和 s3upload.js
合并到同一个文件中的解决方案。如果您真的需要将它们分开,您将需要找到一种不同的方法来 link 套接字连接到 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.id 相同的房间。您可以使用它向特定用户发送消息,但您必须知道 socket.id
与该用户初始化的连接相关联。您必须决定如何管理此关联(通过数据库,或在内存中为其设置一个数组),但是一旦拥有它,只需通过以下方式发送进度百分比:
socket.broadcast.to( user_socket_id ).emit( "progress", number_or_percent );