Enviar mensagem para um cliente específico com socket.io e node.js

Eu estou trabalhando com socket.io e node.js e até agora parece muito bom, mas eu não sei como enviar uma mensagem do servidor para um cliente específico, algo assim:

client.send(message, receiverSessionId) 

Mas nem os .send() nem o .broadcast() parecem suprir minha necessidade.

O que eu encontrei como uma solução possível, é que o método .broadcast() aceita como um segundo parâmetro um array de SessionIds para o qual não envia a mensagem, então eu poderia passar um array com todos os SessionIds conectados naquele momento para o servidor , exceto aquele que desejo enviar a mensagem, mas sinto que deve haver uma solução melhor.

Alguma ideia?

Bem, você tem que pegar o cliente para isso (surpresa), você pode ir da maneira mais simples:

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

O que pode quebrar, dificilmente duvido, mas é sempre uma possibilidade que io.clients podem ser alterados, então use o acima com cuidado

Ou você mesmo controla os clientes, portanto, você os adiciona ao seu próprio object de clients no ouvinte de connection e os remove no ouvinte de disconnect .

Eu usaria o último, uma vez que dependendo da sua aplicação você pode querer ter mais estado no para os clientes de qualquer maneira, então algo como clients[id] = {conn: clientConnect, data: {...}} pode fazer o trabalho.

A resposta de Ivo Wetzel parece não ser mais válida no Socket.io 0.9.

Em resumo, você deve salvar o socket.id e usar io.sockets.socket(savedSocketId).emit(...) para enviar mensagens para ele.

Foi assim que consegui trabalhar com o servidor Node.js em cluster:

Primeiro, você precisa definir a loja Redis como a loja para que as mensagens possam passar por processos cruzados:

 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); }); }); }); 

Eu omiti o código de cluster aqui, porque isso torna isso mais confuso, mas é trivial adicionar. Basta adicionar tudo ao código do trabalhador. Mais docs aqui http://nodejs.org/api/cluster.html

cada soquete se junta a uma sala com um id de soquete para um nome, para que você possa

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

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

Felicidades

A solução mais simples e elegante

É tão fácil como:

 client.emit("your message"); 

E é isso.

Mas como? Me dê um exemplo

O que todos nós precisamos é, de fato, um exemplo completo, e é o que segue. Isso é testado com a versão mais recente do socket.io (2.0.3) e também está usando Javascript moderno (que todos nós deveríamos usar agora).

O exemplo é composto de duas partes: um servidor e um cliente. Sempre que um cliente se conecta, ele começa a receber do servidor um número de sequência periódico. Uma nova sequência é iniciada para cada novo cliente, portanto, o servidor deve rastreá-las individualmente. É aí que o “eu preciso enviar uma mensagem para um determinado cliente” entra em jogo. O código é muito simples de entender. Vamos ver isso.

Servidor

server.js

 const io = require("socket.io"), server = io.listen(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); 

O servidor começa a escutar na porta 8000 para conexões de input. Quando alguém chega, adiciona esse novo cliente a um mapa para que ele possa acompanhar o seu número de sequência. Ele também escuta o evento de disconnect desse cliente, quando ele será removido do mapa.

A cada segundo, um timer é triggersdo. Quando isso acontece, o servidor percorre o mapa e envia uma mensagem para cada cliente com seu número de sequência atual. Em seguida, ele é incrementado e armazena o número de volta no mapa. Isso é tudo o que é para isso. Mole-mole.

Cliente

A parte do cliente é ainda mais simples. Ele apenas se conecta ao servidor e ouve a mensagem seq-num , imprimindo-a no console toda vez que ela chega.

client.js

 const io = require("socket.io-client"), ioClient = io.connect("http://localhost:8000"); ioClient.on("seq-num", (msg) => console.info(msg)); 

Executando o exemplo

Instale as bibliotecas necessárias:

 npm install socket.io npm install socket.io-client 

Execute o servidor:

 node server 

Abra outras janelas de terminal e crie quantos clientes desejar, executando:

 node client 

Eu também preparei uma essência com o código completo aqui .

Em 1.0 você deve usar:

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

Você pode usar

// envia mensagem apenas para o remetente-cliente

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

// ou você pode enviar para todos os ouvintes, incluindo o remetente

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

// enviar para todos os ouvintes, exceto o remetente

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

// ou você pode mandar para um quarto

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

Seja qual for a versão que estamos usando, se apenas console.log () o object “io” que usamos em nosso código nodejs do lado do servidor, [por exemplo, io.on (‘connection’, function (socket) {…});] , podemos ver que “io” é apenas um object json e há muitos objects-filhos em que os objects id de soquete e soquete são armazenados.

Eu estou usando o socket.io versão 1.3.5, btw.

Se olharmos no object io, ele contém

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

aqui podemos ver os socketids “B5AC9w0sYmOGWe4fAAAA” etc. Então, nós podemos fazer,

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

Mais uma vez, em uma inspeção mais detalhada, podemos ver segmentos como

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

Então, podemos recuperar um soquete daqui fazendo

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

Além disso, sob o motor temos,

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

Então, nós também podemos escrever

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

Então, eu acho que podemos alcançar nosso objective em qualquer uma das três maneiras que eu listei acima,

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

Você consegue fazer isso

No servidor.

 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 "}); } 

E no cliente, é muito fácil de manusear.

 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) }) 

A partir da versão 1.4.5, certifique-se de fornecer um socketId prefixado em io.to (). Eu estava tomando o socketId do cliente logado para depurar e ficou sem prefixo então acabei pesquisando para sempre até que descobri! Então, você pode ter que fazer assim se o ID que você tiver não for prefixado:

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

io.sockets.sockets [socket.id] .emit (…) funcionou para mim na v0.9

Socket.IO permite que você “namespace” seus sockets, o que significa essencialmente atribuir diferentes endpoints ou caminhos.

Isso pode ajudar: http://socket.io/docs/rooms-and-namespaces/