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

Send message to specific client with socket.io and node.js

提问人:Rodolfo Palma 提问时间:1/10/2011 最后编辑:Lucio PaivaRodolfo Palma 更新时间:12/24/2022 访问量:346379

问:

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

client.send(message, receiverSessionId)

但这两种方法似乎都不能满足我的需求。.send().broadcast()

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

有什么想法吗?

JavaScript node.js websocket socket.io 服务器端

评论


答:

99赞 Ivo Wetzel 1/10/2011 #1

好吧,你必须抓住客户(惊喜),你可以走简单的方法:

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

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

或者你自己跟踪客户端,因此你把它们添加到侦听器中你自己的对象中,并在侦听器中删除它们。clientsconnectiondisconnect

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

评论

9赞 AndrewHenderson 1/1/2013
伊沃,你能举一个更完整的例子或详细说明一下吗?我渴望了解这种方法,但我不确定我是否能识别您在此示例中使用的变量/对象。在 clients[id] = {conn: clientConnect, data: {...}}, clients[id] 是上面 io.clients[sessionID] 中所示的 io 对象的一部分吗?还有什么是 clientConnect 对象?谢谢。
203赞 esamatti 3/22/2012 #2

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

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

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

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

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

评论

5赞 Adrien Schuler 6/25/2012
谢谢,很有帮助。我只需要使用一个数组:(不知道是不是因为我使用的是命名空间)io.of('/mynamespace').sockets[socketID].emit(...)
0赞 Gal Ben-Haim 1/17/2013
在集群环境中,如何确保套接字所属的正确进程正在发送消息?
0赞 matanster 7/21/2013
由 NGINX 或 HAProxy @Gal Ben-Haim 提供的粘性会话怎么样?
0赞 Lucas Bertollo 11/28/2015
var holder = 新的 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) 在 Module.load (module.js:355:32) 在 Function.Module._load (module.js:310:12) 在 Function.Module.runMain (module.js:501:10) 在启动时 (node.js:129:16) 在 node.js:814:3
2赞 ImMathan 3/2/2017
io.sockets.socket(socket_id)在 socket.io 1.0 中删除。github.com/socketio/socket.io/issues/1618#issuecomment-46151246
6赞 aljosa 2/1/2014 #3

io.sockets.sockets[socket.id].emit(...) 在 v0.9 中对我有用

评论

0赞 jerry 2/1/2014
欢迎来到 Stack Overflow。这个答案似乎并没有给现有的答案增加太多的相关性。一旦你有了更多的声誉,你就可以对其他人的帖子发表评论。这似乎更适合发表评论。
104赞 Lucio Paiva 6/16/2014 #4

最简单、最优雅的方式

经验证可与 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 [email protected] [email protected]

运行服务器:

node server

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

node client

我还在这里准备了一个包含完整代码的要点。

评论

0赞 pyfork 4/30/2016
端口 8000 是 socketio 在生产环境中的标准吗?
1赞 Lucio Paiva 8/3/2016
对不起,我花了这么长时间才回复,@ingo。8000 是 Node 社区在测试 websocket 或 HTTP 服务器时使用的通用端口(3000 也很常见)。当然,您可以在生产中使用它,但我不确定这有多普遍......无论如何,只要您的网关/负载均衡器/等为此做好准备,您就可以真正使用任何端口。
0赞 Umair Ayub 9/11/2018
我是 Python/PHP 程序员,我是套接字和节点的新手,问题是,为什么我们需要每秒增加同一用户的序列数?那只是为了演示吗?
1赞 Lucio Paiva 9/11/2018
@Umair它只是用于演示目的,但实际上不需要增加序列号。这只是为了表明您可以继续向每个客户发送东西,他们会收到它。
0赞 sçuçu 10/9/2018
在此示例中,我没有看到特定客户端如何仅向另一个客户端发送消息。每个客户端只知道您为演示命名和创建时自己的序列号,并且没有从此序列号到套接字 ID 的映射,而将消息直接发送到具有该套接字 ID 的客户端所需的套接字 ID 是必需的。
41赞 PHPthinking 10/30/2014 #5

在 1.0 中,您应该使用:

io.sockets.connected[socketid].emit();

评论

1赞 Fraggle 3/30/2015
是的!!,以前 io.sockets.sockets[socketid].emit() 有效,但这在较新版本的 socket.io 中给了我未定义的对象错误。更改为 io.sockets.connected 有效。
0赞 Avi Cherry 6/26/2015
对于那些使用 TypeScript 的人来说,根据类型,这是目前用于此的“规范”API。
0赞 Raz 1/11/2018
但是我收到一个错误: io.sockets.connected[“JgCoFX9AiCND_ZhdAAAC”].emit(“socketFromServer”, info);^ TypeError:无法读取未定义的属性“emit”
2赞 sçuçu 10/9/2018
这可能是由于 API 更新了,并且不再有像 and its method 这样的对象。io.sockets.connected["something"]emit
146赞 Matic Kogovšek 12/3/2014 #6

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

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

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

评论

11赞 blented 7/15/2015
这是最好的答案,适用于较新版本的 socket.io。这里有一个很好的备忘单:stackoverflow.com/questions/10058226/......
6赞 Tanase Butcaru 5/25/2016
请注意,这是一种“广播”类型的事件。因此,如果您尝试将回调设置为此,则会出现错误。如果需要通过回调将事件发送到特定套接字,请使用 @PHPthinking 的 answer 并使用 .已使用 1.4.6 版本进行测试。io.sockets.connected[socketid].emit();
0赞 Raz 1/11/2018
但是我收到此错误: io.to[“JgCoFX9AiCND_ZhdAAAC”].emit(“socketFromServe r”, info);^ TypeError:无法读取未定义的属性“emit”
0赞 DORRITO 1/17/2021
这对我有用,但必须使用 io.to(id).emit(“keyword”, data)。
1赞 Igor 1/13/2015 #7

Socket.IO 允许您“命名”套接字,这实质上意味着分配不同的终结点或路径。

这可能会有所帮助:http://socket.io/docs/rooms-and-namespaces/

15赞 Suman Barick 6/17/2015 #8

无论我们使用什么版本,如果我们只是console.log()我们在服务器端nodejs代码中使用的“io”对象,[例如io.on('connection', function(socket) {...});],我们可以看到“io”只是一个json对象,并且有许多子对象存储了套接字ID和套接字对象。

顺便说一句,我正在使用 socket.io 1.3.5 版本。

如果我们看一下 io 对象,它包含,

 sockets:
  { name: '/',
    server: [Circular],
    sockets: [ [Object], [Object] ],
    connected:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

在这里我们可以看到套接字ids“B5AC9w0sYmOGWe4fAAAA”等。所以,我们可以做到,

io.sockets.connected[socketid].emit();

同样,在进一步检查时,我们可以看到这样的部分,

 eio:
  { clients:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

因此,我们可以通过以下方式从这里检索套接字

io.eio.clients[socketid].emit();

此外,在引擎下,我们有,

engine:
 { clients:
    { B5AC9w0sYmOGWe4fAAAA: [Object],
      'hWzf97fmU-TIwwzWAAAB': [Object] },

所以,我们也可以写,

io.engine.clients[socketid].emit();

所以,我想我们可以通过我上面列出的 3 种方式中的任何一种来实现我们的目标,

  1. io.sockets.connected[socketid].emit(); 或
  2. io.eio.clients[socketid].emit(); 或
  3. io.engine.clients[socketid].emit();

评论

0赞 Raz 1/11/2018
但是我收到此错误: io.eio.clients[“JgCoFX9AiCND_ZhdAAAC”].emit(“socketFromServer”, info);^ TypeError:无法读取未定义的属性“emit”
0赞 James 8/17/2018
目前(版本 2.1.1)这仅适用于 sockets.connected,而不适用于其他两个。
0赞 beyondtdr 12/30/2020
有用的答案,让我了解套接字客户端-服务器结构如何更好地工作。谢谢苏曼
13赞 abhaygarg12493 7/28/2015 #9

你可以这样做

在服务器上。

global.io=require("socket.io")(server);

io.on("connection",function(client){
    console.log("client is ",client.id);
    //This is handle by current connected client 
    client.emit('messages',{hello:'world'})
    //This is handle by every client
    io.sockets.emit("data",{data:"This is handle by every client"})
    app1.saveSession(client.id)

    client.on("disconnect",function(){
        app1.deleteSession(client.id)
        console.log("client disconnected",client.id);
    })

})

    //And this is handle by particular client 
    var socketId=req.query.id
    if(io.sockets.connected[socketId]!=null) {
        io.sockets.connected[socketId].emit('particular User', {data: "Event response by particular user "});
    }

在客户端上,它非常容易处理。

var socket=io.connect("http://localhost:8080/")
    socket.on("messages",function(data){
        console.log("message is ",data);
        //alert(data)
    })
    socket.on("data",function(data){
        console.log("data is ",data);
        //alert(data)
    })

    socket.on("particular User",function(data){
        console.log("data from server ",data);
        //alert(data)
    })
42赞 DecoderNT 6/25/2016 #10

你可以使用

仅向发件人客户端发送消息

socket.emit('message', 'check this');

或者您可以发送给包括发送方在内的所有侦听器

io.emit('message', 'check this');

发送给除发送方以外的所有侦听器

socket.broadcast.emit('message', 'this is a message');

或者你可以把它送到一个房间

socket.broadcast.to('chatroom').emit('message', 'this is the message to all');

评论

1赞 iPzard 1/2/2019
对我有用,我还必须在我的server.js文件中添加“const socket = require('socket.io')(http);”。
7赞 risuch 7/22/2016 #11

从版本 1.4.5 开始,请确保在 io.to() 中提供正确前缀的 socketId。 我正在使用客户端记录的socketId进行调试,它没有前缀,所以我最终一直在搜索,直到我发现!因此,如果您拥有的 Id 没有前缀,您可能必须这样做:

io.to('/#' + socketId).emit('myevent', {foo: 'bar'});

评论

0赞 Ebdulmomen1 10/23/2021
我必须删除前缀才能真正工作!谢谢你的回答。
4赞 Kamuran Sönecek 4/16/2020 #12

此外,您还可以保留客户引用。但这会让你的记忆很忙。

创建一个空对象并将客户端设置到其中。

const myClientList = {};

  server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
     myClientList[socket.id] = socket;   
  });
  socket.on("disconnect", (socket) => {
    delete myClientList[socket.id];
  });

然后通过对象的 ID 调用特定客户端

myClientList[specificId].emit("blabla","somedata");