Cómo enviar un mensaje a un cliente en particular con socket.io

95

Estoy comenzando con socket.io + node.js, sé cómo enviar un mensaje localmente y transmitir la socket.broadcast.emit()función: - todos los clientes conectados reciben el mismo mensaje.

Ahora, me gustaría saber cómo enviar un mensaje privado a un cliente en particular, me refiero a un socket para un chat privado entre 2 personas (flujo de cliente a cliente). Gracias.

Nizar B.
fuente
Lo siento psiphi75, pero este enlace no responde a mi respuesta, no es una pregunta duplicada.
Nizar B.
1
@ psiphi75, de ninguna manera es un duplicado
softvar
1
No soporto a gente como @psiphi. ¿Eres siquiera un desarrollador? ¿Cómo se relaciona una pregunta específica de HTML5 con una biblioteca independiente? Y por lo que vale, los WebSockets NO son Socket.io. Socket.io es una biblioteca que puede USAR WebSockets, pero estoy divagando. Esta es también una pregunta más específica relacionada con la biblioteca sobre el envío de datos solo a clientes específicos, no sobre la tecnología en sí.
Levi Roberts
@ bugwheels94 No realmente, esta publicación es de 2011 y dado que nodejs tuvo muchos cambios en el código. esta publicación es definitivamente una pregunta / respuesta válida para este problema.
Nizar B.

Respuestas:

101

Cuando un usuario se conecta, debe enviar un mensaje al servidor con un nombre de usuario que debe ser único, como un correo electrónico.

Un par de nombre de usuario y socket deben almacenarse en un objeto como este:

var users = {
    '[email protected]': [socket object],
    '[email protected]': [socket object],
    '[email protected]': [socket object]
}

En el cliente, emita un objeto al servidor con los siguientes datos:

{
    to:[the other receiver's username as a string],
    from:[the person who sent the message as string],
    message:[the message to be sent as string]
}

En el servidor, escuche los mensajes. Cuando se recibe un mensaje, emita los datos al receptor.

users[data.to].emit('receivedMessage', data)

En el cliente, escuche las emisiones del servidor llamado 'mensaje recibido' y, al leer los datos, puede controlar de quién provienen y el mensaje que se envió.

Adán
fuente
26
Si utiliza esta solución, debe comprender: 1. Cuando el usuario se desconecta, debe limpiar el objeto 'usuarios'. 2. No admite una segunda conexión, por ejemplo, desde otro navegador. Entonces, si el usuario se conecta desde otro navegador, se anulará la conexión anterior.
Vladimir Kurijov
1
¿Cómo almacenarías el objeto de socket en un almacén de datos? Supongo que esto no funciona si tiene más de un proceso de nodo.
Chovy
@chovy tienes que usar redis. Consulte este github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
Vladimir Kurijov
11
Sugeriría no usar esta solución. Terminé perdiendo mucho tiempo tratando de superar las limitaciones de este enfoque. Vea la solución de @ az7ar y esta explicación de por qué es mejor .
Daniel Que
@DanielQue +1. Mucho mejor que utilizar la funcionalidad nativa. ¡Gracias!
Aaron Lelevier
274

Puede utilizar salas socket.io. Desde el lado del cliente, emita un evento ("unirse" en este caso, puede ser cualquier cosa) con cualquier identificador único (correo electrónico, id).

Lado del cliente:

var socket = io.connect('http://localhost');
socket.emit('join', {email: user1@example.com});

Ahora, desde el lado del servidor, use esa información para crear una sala única para ese usuario.

Lado del servidor:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('join', function (data) {
    socket.join(data.email); // We are using room of socket io
  });
});

Entonces, ahora todos los usuarios se han unido a una sala con el nombre del correo electrónico del usuario. Entonces, si desea enviar un mensaje a un usuario específico, solo tiene que

Lado del servidor:

io.sockets.in('[email protected]').emit('new_msg', {msg: 'hello'});

Lo último que queda por hacer en el lado del cliente es escuchar el evento "new_msg".

Lado del cliente:

socket.on("new_msg", function(data) {
    alert(data.msg);
}

Espero que captes la idea.

az7ar
fuente
3
cambie esta línea io.socket.in ('[email protected] '). emit (' new_msg ', {msg:' hello '}); como este io.sockets.in ('[email protected] '). emit (' new_msg ', {msg:' hello '});
silvesterpillarrabu
42
Esta respuesta es mucho mejor que la respuesta actualmente aceptada. He aquí por qué: 1) No tiene que administrar y limpiar la matriz global de clientes. 2) Funciona incluso si un usuario tiene varias pestañas abiertas en la misma página. 3) Se puede ampliar fácilmente para trabajar con un clúster de nodos (múltiples procesos) con socket.io-redis.
Daniel Que
2
¿Cómo se hace {email: [email protected]}diferente para todas las conexiones? ¿No todos los clientes que van a la aplicación tienen un usuario? [email protected]¿ Cómo puedo convertirlo en usuario [email protected]para el segundo cliente que se conecta para poder tener diferentes habitaciones? perdón si esta es una pregunta molesta.
jack en blanco
2
Me encantó la solución, pero creo que la seguridad se ve comprometida aquí. ¿Qué pasa si cambio el correo electrónico en el script en el lado del cliente? Ahora también puedo leer otros mensajes privados. ¿Qué estas diciendo?
Gopinath Shiva
3
no tiene que ser un correo electrónico. Mencioné un identificador único
az7ar
32

SEGURO: Simplemente,

Esto es lo que necesitas :

io.to(socket.id).emit("event", data);

cada vez que un usuario se une al servidor, se generarán los detalles del socket, incluido el ID. Esta es la identificación que realmente ayuda a enviar un mensaje a personas en particular.

primero tenemos que almacenar todos los socket.ids en una matriz,

var people={};

people[name] =  socket.id;

aquí el nombre es el nombre del receptor. Ejemplo:

people["ccccc"]=2387423cjhgfwerwer23;

Entonces, ahora podemos obtener ese socket.id con el nombre del receptor cada vez que enviamos un mensaje:

para esto necesitamos saber el nombre del receptor. Debe emitir el nombre del receptor al servidor.

lo final es:

 socket.on('chat message', function(data){
io.to(people[data.receiver]).emit('chat message', data.msg);
});

Espero que esto funcione bien para ti.

¡¡Buena suerte!!

Troyano
fuente
¿Puede crear un código del lado del cliente y del lado del servidor? Soy nuevo en la programación de scoket. [email protected]
Harish Mahajan
@HARISH, consulte el enlace a continuación, explica lo que realmente está sucediendo desde el lado del cliente ... espero que esto le ayude más ... socket.io/get-started/chat
Trojan
2
Pero, ¿qué pasa si el usuario está chateando con otras 2 personas al mismo tiempo? ¿No vendrá el mensaje de ambos usuarios en ambas ventanas?
Sharan Mohandas
Si hay más de dos usuarios, no funcionará correctamente
Mohhamad Hasham
cada vez que los usuarios conectan el ID de socket cambia, por lo que, en caso de que el usuario abra su aplicación en varias pestañas, obtendrá una respuesta a una que haya almacenado y enviado a otras conexiones de socket
Vikas Kandari
8

Puede referirse a las habitaciones socket.io . Cuando apretó el conector, puede unirlo a la sala con nombre, por ejemplo, "usuario. # {Userid}".

Después de eso, puede enviar un mensaje privado a cualquier cliente con un nombre conveniente, por ejemplo:

io.sockets.in ('user.125'). emit ('new_message', {text: "Hola mundo"})

En la operación anterior enviamos "new_message" al usuario "125".

Gracias.

Vladimir Kurijov
fuente
Hola amigo, gracias por tu primera respuesta, lo intentaré y te lo haré saber, porque no quiero construir un chat sino un mensajero privado como tú puedes usar en la red de Facebook.
Nizar B.
¿Alguien puede ayudarme con este asunto? Estoy realmente atascado, me gustaría agregar un nombre de usuario vinculado a mi socket y permitir que mi aplicación envíe un mensaje a un usuario específico, sin sala de chat, es un mensajero similar a Facebook. ¡Gracias si lo sabes!
Nizar B.
Al principio, debe comprender que este nombre de usuario debe ser único para todos los usuarios. Y el segundo: ¿no se olvidó de suscribirse al mensaje emitido en el lado del cliente?
Vladimir Kurijov
El problema con esto es que pierde la capacidad de usar devoluciones de llamada, ya que no están permitidas en transmisiones, que es lo que realmente está haciendo aquí, una transmisión a su habitación privada.
bluehallu
@VladimirKurijov, el Sr. Katcha no quiere usar Rooms y estoy de acuerdo en que no es la solución a su problema.
miksiii
4

En un proyecto de nuestra empresa estamos usando el enfoque de "habitaciones" y su nombre es una combinación de los identificadores de usuario de todos los usuarios en una conversación como un identificador único (nuestra implementación es más como Facebook Messenger), ejemplo:

| id | nombre | 1 | Scott | 2 | Susan

El nombre de la "habitación" será "1-2" (los identificadores se piden Asc.) y al desconectar socket.io limpia automáticamente la habitación

de esta manera, envía mensajes solo a esa sala y solo a usuarios en línea (conectados) (menos paquetes enviados a través del servidor).

Rami
fuente
1

Permíteme hacerlo más simple con las habitaciones socket.io. solicitar un servidor con un identificador único para unirse a un servidor. aquí estamos usando un correo electrónico como identificador único.

Cliente Socket.io

socket.on('connect', function () {
  socket.emit('join', {email: user@example.com});
});

Cuando el usuario se unió a un servidor, cree una sala para ese usuario

Servidor Socket.io

io.on('connection', function (socket) {
   socket.on('join', function (data) {    
    socket.join(data.email);
  });
});

Ahora estamos listos para unirnos. deje emitir algo desde la tosala de servidores , para que el usuario pueda escuchar.

Servidor Socket.io

io.to('[email protected]').emit('message', {msg: 'hello world.'});

Terminemos el tema escuchando el messageevento por parte del cliente.

socket.on("message", function(data) {
  alert(data.msg);
});

La referencia de las salas Socket.io

Lalit Mohan
fuente