Senden Sie eine Nachricht mit socket.io und node.js an einen bestimmten Client

189

Ich arbeite mit socket.io und node.js und bis jetzt scheint es ziemlich gut zu sein, aber ich weiß nicht, wie ich eine Nachricht vom Server an einen bestimmten Client senden soll, ungefähr so:

client.send(message, receiverSessionId)

Aber weder die .send()noch die .broadcast()Methoden scheinen mein Bedürfnis zu befriedigen.

Als mögliche Lösung habe ich festgestellt, dass die .broadcast()Methode als zweiten Parameter ein Array von SessionIds akzeptiert, an die die Nachricht nicht gesendet wird, sodass ich ein Array mit allen zu diesem Zeitpunkt verbundenen SessionIds an den Server übergeben kann, mit Ausnahme des einen Ich möchte die Nachricht senden, aber ich denke, es muss eine bessere Lösung geben.

Irgendwelche Ideen?

Rodolfo Palma
quelle

Antworten:

95

Nun, dafür muss man sich den Kunden schnappen (Überraschung), man kann entweder den einfachen Weg gehen:

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

Was vielleicht kaputt geht, bezweifle ich, aber es ist immer eine Möglichkeit, io.clientsdie sich ändern könnte. Verwenden Sie die oben genannten Punkte mit Vorsicht

Oder Sie behalten die Clients selbst im Auge, fügen sie Ihrem eigenen clientsObjekt im connectionListener hinzu und entfernen sie im disconnectListener.

Ich würde letzteres verwenden, da Sie abhängig von Ihrer Anwendung möglicherweise ohnehin mehr Status auf den Clients haben möchten, sodass so etwas wie clients[id] = {conn: clientConnect, data: {...}}die Arbeit erledigen könnte.

Ivo Wetzel
quelle
6
Ivo, können Sie auf ein vollständigeres Beispiel verweisen oder etwas näher darauf eingehen? Ich bin gespannt auf diesen Ansatz, bin mir aber nicht sicher, ob ich die Variablen / Objekte erkenne, die Sie in diesem Beispiel verwenden. Ist in clients [id] = {conn: clientConnect, data: {...}} clients [id] Teil des io-Objekts, wie in io.clients [sessionID] oben dargestellt? Was ist auch das clientConnect-Objekt? Vielen Dank.
Andrew Henderson
@ ivo-wetzel hi, kannst du mir zu diesem Thema helfen? stackoverflow.com/questions/38817680/…
Mahdi Pishguy
178

Die Antwort von Ivo Wetzel scheint in Socket.io 0.9 nicht mehr gültig zu sein.

Kurz gesagt, Sie müssen das jetzt speichern socket.idund verwenden io.sockets.socket(savedSocketId).emit(...) , um Nachrichten an es zu senden.

So habe ich das auf dem Clustered Node.js Server zum Laufen gebracht:

Zuerst müssen Sie den Redis-Speicher als Speicher festlegen, damit Nachrichten prozessübergreifend ausgeführt werden können:

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

});

Ich habe den Clustering-Code hier weggelassen, weil er dadurch unübersichtlicher wird, aber das Hinzufügen ist trivial. Fügen Sie einfach alles zum Worker-Code hinzu. Weitere Dokumente finden Sie hier http://nodejs.org/api/cluster.html

Epeli
quelle
4
Danke, es war hilfreich. Ich musste stattdessen nur ein Array verwenden: io.of('/mynamespace').sockets[socketID].emit(...)(weiß nicht, ob es daran liegt, dass ich einen Namespace verwende)
Adrien Schuler
Wie stelle ich in einer Clusterumgebung sicher, dass der richtige Prozess, zu dem der Socket gehört, das Senden der Nachricht ist?
Gal Ben-Haim
Wie wäre es mit einer klebrigen Sitzung mit freundlicher Genehmigung von NGINX oder HAProxy @Gal Ben-Haim?
Matanster
var Inhaber = neuer Socketio.RedisStore; ^ TypeError: undefined ist keine Funktion bei Object. <Anonym> (C: \ Benutzer \ Dev \ Desktop \ nouty-server \ server.js: 108: 14) bei Module._compile (module.js: 460: 26) bei Object.Module._extensions..js (module.js: 478: 10) bei Module.load (module.js: 355: 32) bei Function.Module._load (module.js: 310: 12) bei Function.Module. runMain (module.js: 501: 10) beim Start (node.js: 129: 16) bei node.js: 814: 3
Lucas Bertollo
1
io.sockets.socket(socket_id)wird in socket.io 1.0 entfernt. github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan
96

Jeder Socket verbindet einen Raum mit einer Socket-ID für einen Namen, sodass Sie dies einfach tun können

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

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

Prost

Matic Kogovšek
quelle
7
Dies ist die beste Antwort und funktioniert mit neueren Versionen von socket.io. Es gibt hier einen schönen Spickzettel: stackoverflow.com/questions/10058226/…
Blented
4
Beachten Sie, dass dies ein Broadcast-Typ ist, der ein Ereignis aussendet. Wenn Sie also versuchen, einen Rückruf festzulegen, wird ein Fehler angezeigt. Wenn Sie ein Ereignis mit einem Rückruf an einen bestimmten Socket senden müssen, verwenden Sie die Antwort von @ PHPthinking und verwenden Sie io.sockets.connected[socketid].emit();. Getestet mit 1.4.6.
Butcaru
Aber ich habe diesen Fehler bekommen: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServe‌ r", info); ^ TypeError: Eigenschaft 'emit' von undefined kann nicht gelesen werden
Raz
85

Die einfachste und eleganteste Lösung

Es ist so einfach wie:

client.emit("your message");

Und das ist es.

Aber wie? Gib mir ein Beispiel

Was wir alle brauchen, ist in der Tat ein vollständiges Beispiel, und das folgt. Dies wird mit der neuesten socket.io-Version (2.0.3) getestet und es wird auch modernes Javascript verwendet (das wir jetzt alle verwenden sollten).

Das Beispiel besteht aus zwei Teilen: einem Server und einem Client. Immer wenn ein Client eine Verbindung herstellt, erhält er vom Server eine periodische Sequenznummer. Für jeden neuen Client wird eine neue Sequenz gestartet, sodass der Server diese einzeln verfolgen muss. Hier kommt das "Ich muss eine Nachricht an einen bestimmten Kunden senden" ins Spiel. Der Code ist sehr einfach zu verstehen. Mal sehen.

Server

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

Der Server überwacht Port 8000 auf eingehende Verbindungen. Wenn einer ankommt, fügt er diesen neuen Client einer Karte hinzu, damit er seine Sequenznummer verfolgen kann. Es hört auch auf diesen Kundendisconnect Ereignis , wenn es von der Karte entfernt wird.

Jede Sekunde wird ein Timer ausgelöst. In diesem Fall durchläuft der Server die Karte und sendet an jeden Client eine Nachricht mit der aktuellen Sequenznummer. Es erhöht es dann und speichert die Nummer wieder in der Karte. Das ist alles was dazu gehört. Kinderleicht.

Klient

Der Client-Teil ist noch einfacher. Es stellt lediglich eine Verbindung zum Server her, wartet auf die seq-numNachricht und druckt sie bei jedem Eintreffen auf der Konsole aus.

client.js

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

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

Beispiel ausführen

Installieren Sie die erforderlichen Bibliotheken:

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

Führen Sie den Server aus:

node server

Öffnen Sie andere Terminalfenster und erzeugen Sie so viele Clients, wie Sie möchten, indem Sie Folgendes ausführen:

node client

Ich habe auch bereit , einen Kern mit dem vollständigen Code hier .

Lucio Paiva
quelle
Ist Port 8000 die Norm für Socketio in der Produktion?
Red
1
Entschuldigung, ich habe so lange gebraucht, um zu antworten, @ingo. 8000 ist ein allgemeiner Port, der von der Node-Community beim Testen von Websockets oder HTTP-Servern verwendet wird (3000 ist ebenfalls üblich). Natürlich könnten Sie es in der Produktion verwenden, aber ich bin mir nicht sicher, wie häufig das ist ... Sie können jeden beliebigen Port wirklich verwenden, solange Ihre Gateways / Load Balancer / usw. darauf vorbereitet sind.
Lucio Paiva
Ich bin ein Python / PHP-Programmierer und ich bin neu in Sockets und Knoten. Die Frage ist, warum wir die Sequenznummer desselben Benutzers pro Sekunde erhöhen müssen. ist das nur für die Demo?
Umair
1
@Umair Es ist nur für Demozwecke, keine wirkliche Notwendigkeit, eine Sequenznummer zu erhöhen. Es sollte nur zeigen, dass Sie weiterhin Inhalte an jeden Kunden senden können und dieser diese erhalten wird.
Lucio Paiva
In diesem Beispiel wird nicht angezeigt, wie ein bestimmter Client eine Nachricht nur und nur an einen anderen Client sendet. Jeder Client kennt nur seine eigene Sequenznummer, wie Sie sie benannt und für die Demo erstellt haben, und es gibt keine Zuordnung dieser Sequenznummern zu einer Socket-ID, die erforderlich ist, um eine Nachricht mit dieser Socket-ID direkt an den Client zu senden.
Selçuk
35

Sie können verwenden

// Nachricht nur an Absender-Client senden

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

// oder Sie können an alle Listener einschließlich des Absenders senden

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

// an alle Listener außer dem Absender senden

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

// oder du kannst es in einen Raum schicken

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

DecoderNT
quelle
Bei mir musste ich auch "const socket = require ('socket.io') (http);" hinzufügen. in meiner server.js Datei.
iPzard
35

In 1.0 sollten Sie verwenden:

io.sockets.connected[socketid].emit();
PHPthinking
quelle
1
Ja !!!, zuvor funktionierte io.sockets.sockets [socketid] .emit (), aber dies gab mir undefinierte Objektfehler in der neueren Version von socket.io. Der Wechsel zu io.sockets.connected funktioniert.
Fraggle
Für diejenigen, die TypeScript verwenden, ist dies derzeit die 'kanonische' API für diese gemäß den Typisierungen.
Avi Cherry
Ich erhalte jedoch eine Fehlermeldung: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Eigenschaft 'emit' von undefined kann nicht gelesen werden
Raz
Das liegt wahrscheinlich daran, dass die API aktualisiert wurde und es kein Objekt mehr wie io.sockets.connected["something"]und seine emitMethode gibt.
Selçuk
12

Welche Version wir auch verwenden, wenn wir nur console.log () das "io" -Objekt verwenden, das wir in unserem serverseitigen nodejs-Code verwenden, [z. B. io.on ('connection', function (socket) {...});] können wir sehen, dass "io" nur ein JSON-Objekt ist und es viele untergeordnete Objekte gibt, in denen die Socket-ID und die Socket-Objekte gespeichert sind.

Ich benutze übrigens socket.io Version 1.3.5.

Wenn wir in das io-Objekt schauen, enthält es:

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

Hier sehen wir die Socketids "B5AC9w0sYmOGWe4fAAAA" usw. Also können wir tun,

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

Bei weiterer Betrachtung sehen wir wieder Segmente wie:

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

So können wir von hier aus einen Socket abrufen

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

Auch unter Motor haben wir,

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

So können wir auch schreiben,

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

Ich denke, wir können unser Ziel auf eine der drei oben genannten Arten erreichen:

  1. io.sockets.connected [socketid] .emit (); ODER
  2. io.eio.clients [socketid] .emit (); ODER
  3. io.engine.clients [socketid] .emit ();
Suman Barick
quelle
Aber ich habe diesen Fehler bekommen: io.eio.clients ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", info); ^ TypeError: Eigenschaft 'emit' von undefined kann nicht gelesen werden
Raz
Derzeit (mit Version 2.1.1) funktionierte dies nur für sockets.connected, nicht jedoch für die beiden anderen.
James
10

Du kannst das

Auf dem Server.

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

Und für den Kunden ist es sehr einfach zu handhaben.

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

Stellen Sie ab Version 1.4.5 sicher, dass Sie in io.to () eine ordnungsgemäß vorangestellte socketId angeben. Ich habe die Socket-ID genommen, die der Client zum Debuggen angemeldet hat, und sie war ohne Präfix, sodass ich für immer gesucht habe, bis ich es herausgefunden habe! Möglicherweise müssen Sie dies folgendermaßen tun, wenn der ID, die Sie haben, kein Präfix vorangestellt ist:

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

io.sockets.sockets [socket.id] .emit (...) hat in Version 0.9 für mich funktioniert

Aljosa
quelle
Willkommen bei Stack Overflow. Diese Antwort scheint den vorhandenen Antworten nicht viel Relatve hinzuzufügen. Sobald Sie mehr Ruf haben , können Sie die Beiträge anderer Leute kommentieren . Dies scheint besser für einen Kommentar geeignet zu sein.
Jerry
2

Sie können auch Kundenreferenzen behalten. Aber das macht deine Erinnerung beschäftigt.

Erstellen Sie ein leeres Objekt und legen Sie Ihre Kunden darin fest.

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

Rufen Sie dann Ihren spezifischen Client anhand der ID des Objekts auf

myClientList[specificId].emit("blabla","somedata");
Kamuran Sönecek
quelle
0

Mit Socket.IO können Sie Ihre Sockets mit einem „Namespace“ versehen, was im Wesentlichen bedeutet, dass verschiedene Endpunkte oder Pfade zugewiesen werden.

Dies könnte helfen: http://socket.io/docs/rooms-and-namespaces/

Igor T.
quelle