使用 socket.io 和 node.js 向特定客户端发送消息

IT技术 javascript node.js websocket socket.io server-side
2021-01-14 18:07:45

我正在使用 socket.io 和 node.js ,直到现在看起来还不错,但我不知道如何从服务器向特定客户端发送消息,如下所示:

client.send(message, receiverSessionId)

但是无论是方法.send()还是.broadcast()方法似乎都不能满足我的需要。

我发现的一个可能的解决方案是,该.broadcast()方法接受一个不向其发送消息的 SessionIds 数组作为第二个参数,因此我可以将一个包含当时连接的所有 SessionIds 的数组传递给服务器,除了一个我希望发送消息,但我觉得必须有更好的解决方案。

有任何想法吗?

6个回答

Ivo Wetzel 的回答在 Socket.io 0.9 中似乎不再有效。

简而言之,您现在必须保存socket.id并用于io.sockets.socket(savedSocketId).emit(...) 向其发送消息。

这就是我在集群 Node.js 服务器中工作的方式:

首先,您需要将 Redis 存储设置为存储,以便消息可以跨进程:

var express = require("express");
var redis = require("redis");
var sio = require("socket.io");

var client = redis.createClient()
var app = express.createServer();
var io = sio.listen(app);

io.set("store", new sio.RedisStore);


// In this example we have one master client socket 
// that receives messages from others.

io.sockets.on('connection', function(socket) {

  // Promote this socket as master
  socket.on("I'm the master", function() {

    // Save the socket id to Redis so that all processes can access it.
    client.set("mastersocket", socket.id, function(err) {
      if (err) throw err;
      console.log("Master socket is now" + socket.id);
    });
  });

  socket.on("message to master", function(msg) {

    // Fetch the socket id from Redis
    client.get("mastersocket", function(err, socketId) {
      if (err) throw err;
      io.sockets.socket(socketId).emit(msg);
    });
  });

});

我在这里省略了聚类代码,因为它使这更混乱,但添加起来很简单。只需将所有内容添加到工作代码中。更多文档在这里http://nodejs.org/api/cluster.html

谢谢它很有帮助。我只需要改用一个数组:(io.of('/mynamespace').sockets[socketID].emit(...)不知道是不是因为我使用的是命名空间)
2021-03-20 18:07:45
io.sockets.socket(socket_id)在 socket.io 1.0 中被删除。github.com/socketio/socket.io/issues/1618#issuecomment-46151246
2021-03-31 18:07:45
在集群环境中,如何确保套接字所属的正确进程正在发送消息?
2021-04-07 18:07:45
由 NGINX 或 HAProxy @Gal Ben-Haim 提供的粘性会话怎么样?
2021-04-07 18:07:45
var holder = new socketio.RedisStore; ^ TypeError: undefined is not a function at Object.<anonymous> (C:\Users\Dev\Desktop\nouty-server\server.js:108:14) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module。 runMain (module.js:501:10) 在启动 (node.js:129:16) 在 node.js:814:3
2021-04-07 18:07:45

每个套接字加入一个带有套接字 ID 作为名称的房间,因此您可以

io.to('socket#id').emit('hey')

文档:http : //socket.io/docs/rooms-and-namespaces/#default-room

干杯

这是最好的答案,适用于较新版本的 socket.io。这里有一个不错的备忘单:stackoverflow.com/questions/10058226/...
2021-03-12 18:07:45
但是我收到了这个错误:io.to["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServe‌ r", info); ^ 类型错误:无法读取未定义的属性“emit”
2021-03-23 18:07:45
请注意,这是一种“广播”类型的发出事件。因此,如果您尝试为此设置回调,则会出现错误。如果您需要使用回调将事件发送到特定套接字,请使用@PHPthinking 的答案并使用io.sockets.connected[socketid].emit();. 用 1.4.6 测试。
2021-03-25 18:07:45
这对我有用,但必须使用 io.to(id).emit("keyword", data)。
2021-04-07 18:07:45

最简单、最优雅的方式

已验证与 socket.io v3.1.1 一起使用

这很简单:

client.emit("your message");

就是这样。好的,但它是如何工作的?

最小工作示例

下面是一个简单的客户端-服务器交互示例,其中每个客户端都会定期收到一条包含序列号的消息。每个客户端都有一个独特的序列,这就是“我需要向特定客户端发送消息”的地方

服务器

server.js

const
    {Server} = require("socket.io"),
    server = new Server(8000);

let
    sequenceNumberByClient = new Map();

// event fired every time a new client connects:
server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
    // initialize this client's sequence number
    sequenceNumberByClient.set(socket, 1);

    // when socket disconnects, remove it from the list:
    socket.on("disconnect", () => {
        sequenceNumberByClient.delete(socket);
        console.info(`Client gone [id=${socket.id}]`);
    });
});

// sends each client its current sequence number
setInterval(() => {
    for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
        client.emit("seq-num", sequenceNumber);
        sequenceNumberByClient.set(client, sequenceNumber + 1);
    }
}, 1000);

服务器开始在端口 8000 上侦听传入连接。一旦建立了新的连接,该客户端就会被添加到一个记录其序列号的映射中。disconnect当客户端离开时,服务器还会监听从地图中移除客户端事件。

每一秒,都会触发一个计时器。当它发生时,服务器遍历映射并向每个客户端发送带有当前序列号的消息,然后立即增加它。这就是它的全部内容。十分简单。

客户

客户端部分甚至更简单。它只是连接到服务器并侦听seq-num消息,每次到达时将其打印到控制台。

client.js

const
    io = require("socket.io-client"),
    ioClient = io.connect("http://localhost:8000");

ioClient.on("seq-num", (msg) => console.info(msg));

运行示例

安装所需的库:

npm install socket.io@3.1.1 socket.io-client@3.1.1

运行服务器:

node server

通过运行以下命令打开其他终端窗口并生成任意数量的客户端:

node client

我也准备与全电码梗概这里

在此示例中,我没有看到特定客户端如何仅向另一个客户端发送消息。每个客户端只知道自己为演示命名和创建的序列号,并且没有从这个序列号到套接字 id 的映射,需要将消息直接发送到具有该套接字 id 的客户端。
2021-03-12 18:07:45
端口 8000 是生产中 socketio 的标准吗?
2021-03-13 18:07:45
抱歉,我花了这么长时间才回复,@ingo。8000 是 Node 社区在测试 websocket 或 HTTP 服务器时使用的常用端口(3000 也很常见)。当然,您可以在生产中使用它,但我不确定它有多常见……无论如何,您可以真正使用任何端口,只要您的网关/负载平衡器/等已为此做好准备。
2021-03-24 18:07:45
我是 Python/PHP 程序员,我是套接字和节点的新手,问题是,为什么我们需要每秒增加同一用户的 seq 数?只是为了演示吗?
2021-04-06 18:07:45
@Umair 仅用于演示目的,实际上不需要增加序列号。这只是为了表明您可以继续向每个客户发送内容,他们会收到。
2021-04-09 18:07:45

好吧,您必须为此抓住客户(惊喜),您可以采用简单的方法:

var io = io.listen(server);
io.clients[sessionID].send()

这可能会中断,我对此表示怀疑,但总是有可能io.clients会发生变化,因此请谨慎使用上述内容

或者您自己跟踪客户端,因此您将它们添加到您自己clientsconnection侦听器对象中并在侦听器中删除它们disconnect

我会使用后一个,因为根据您的应用程序,无论如何您可能希望在客户端上拥有更多状态,因此类似的事情clients[id] = {conn: clientConnect, data: {...}}可能会完成这项工作。

Ivo,你能指出一个更完整的例子或详细说明一下吗?我很想了解这种方法,但我不确定我是否认识您在此示例中使用的变量/对象。在clients[id] = {conn: clientConnect, data: {...}} 中,clients[id] 是否是io 对象的一部分,如上面的io.clients[sessionID] 所示?还有什么是 clientConnect 对象?谢谢。
2021-03-23 18:07:45
@ivo-wetzel 嗨,你能帮我谈谈这个话题吗?stackoverflow.com/questions/38817680/...
2021-04-06 18:07:45

在 1.0 中,您应该使用:

io.sockets.connected[socketid].emit();
对于那些使用 TypeScript 的人,根据类型,这是当前的“规范”API。
2021-03-13 18:07:45
但我收到一个错误: io.sockets.connected["JgCoFX9AiCND_ZhdAAAC"].emit("socketFromServer", info); ^ 类型错误:无法读取未定义的属性“emit”
2021-03-20 18:07:45
是的!!!,以前 io.sockets.sockets[socketid].emit() 工作,但这在较新版本的 socket.io 中给了我未定义的对象错误。更改为 io.sockets.connected 有效。
2021-03-25 18:07:45
这可能是由于 api 更新了,并且不再有类似的对象io.sockets.connected["something"]及其emit方法。
2021-04-05 18:07:45