Senden einer Nachricht an einen bestimmten verbundenen Benutzer mit webSocket?

81

Ich habe einen Code zum Senden einer Nachricht an alle Benutzer geschrieben:

// websocket and http servers
var webSocketServer = require('websocket').server;

...
...
var clients = [ ];

var server = http.createServer(function(request, response) {
    // Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
  ...
});

var wsServer = new webSocketServer({
    // WebSocket server is tied to a HTTP server. 
    httpServer: server
});

// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
...
var connection = request.accept(null, request.origin); 
var index = clients.push(connection) - 1;
...

Bitte beachten Sie:

  • Ich habe keine Benutzerreferenz, sondern nur eine Verbindung.
  • Alle Benutzerverbindungen werden in einem gespeichert array.

Ziel : Angenommen, der Node.js-Server möchte eine Nachricht an einen bestimmten Client (John) senden. Wie würde der NodeJs-Server wissen, welche Verbindung John hat? Der Node.js-Server kennt John nicht einmal. Alles, was es sieht, sind die Verbindungen.

Daher glaube ich, dass ich Benutzer jetzt nicht nur über ihre Verbindung speichern sollte, sondern stattdessen ein Objekt speichern muss, das das userIdund das connectionObjekt enthält.

Idee:

  • Wenn die Seite vollständig geladen ist (DOM-fähig), stellen Sie eine Verbindung zum Node.js-Server her.

  • Wenn der Node.js-Server eine Verbindung akzeptiert, generieren Sie eine eindeutige Zeichenfolge und senden Sie sie an den Client-Browser. Speichern Sie die Benutzerverbindung und die eindeutige Zeichenfolge in einem Objekt. z.B{UserID:"6", value: {connectionObject}}

  • Wenn diese Nachricht auf der Clientseite eintrifft, speichern Sie sie in einem versteckten Feld oder Cookie. (für zukünftige Anfragen an den NodeJs-Server)


Wenn der Server eine Nachricht an John senden möchte:

  • Suchen Sie Johns UserID im Wörterbuch und senden Sie eine Nachricht über die entsprechende Verbindung.

  • Bitte beachten Sie, dass hier (im Nachrichtenmechanismus) kein asp.net-Servercode aufgerufen wird. nur NodeJs. *

Frage:

Ist das der richtige Weg?

Royi Namir
quelle

Antworten:

75

Dies ist nicht nur der richtige Weg, sondern auch der einzige. Grundsätzlich benötigt jede Verbindung eine eindeutige ID. Andernfalls können Sie sie nicht identifizieren. So einfach ist das.

Nun, wie Sie es darstellen werden, ist eine andere Sache. Ein Objekt mit idund connectionEigenschaften zu erstellen ist ein guter Weg, dies zu tun (ich würde es definitiv versuchen). Sie können das idObjekt auch direkt an das Verbindungsobjekt anhängen .

Denken Sie auch daran, dass Sie, wenn Sie Kommunikation zwischen Benutzern wünschen, auch die ID des Zielbenutzers senden müssen, dh wenn Benutzer A eine Nachricht an Benutzer B senden möchte, muss A offensichtlich die ID von B kennen.

verrückt
quelle
1
@ RoyiNamir Auch hier haben Sie nicht viel Auswahl. Der Browser beendet die Verbindung, sodass Sie das Trennen und Wiederverbinden der Verbindung bewältigen müssen. Außerdem möchten Sie wahrscheinlich einen Timeout-Mechanismus implementieren (zerstören Sie das Wrapper-Objekt auf der Serverseite erst nach dem Timeout), um unnötige Benachrichtigungen zu vermeiden, wenn dies geschieht (vorausgesetzt, Sie möchten andere Benutzer über die Trennung benachrichtigen).
freakish
1
@RoyiNamir Was zwei eindeutige Namen betrifft: Die andere Idee besteht darin, das Objekt SOCKETS = {};auf der Serverseite zu belassen und einer Liste einen Socket für eine Give-ID hinzuzufügen, dh SOCKETS["John"] = [];und SOCKETS["John"].push(conn);. Sie werden das wahrscheinlich brauchen, da ein Benutzer mehrere Registerkarten öffnen kann.
freakish
8
@freakish: Achtung, dies ist nur eine gute Lösung für eine Ein-Prozess-App. Wenn Sie Ihre Anwendung mit mehreren Prozessen (oder Servern) skalieren, teilen sich diese nicht denselben Speicher. Daher funktioniert die Iteration mit lokal gespeicherten Objekten nicht. Es ist besser, alle lokalen Verbindungen direkt zu iterieren und die UUID in der Verbindung selbst zu speichern. Auch für die Skalierung würde ich Redis empfehlen.
Myst
1
Können Sie mir bitte sagen, wie ich mithilfe eines Verbindungsobjekts eine Nachricht an einen bestimmten Client senden soll? Ich habe die Benutzer-ID wie oben angegeben im Verbindungsobjekt gespeichert. Ich möchte nur wissen, wie man ein Verbindungsobjekt verwendet, während eine Nachricht vom Server an einen bestimmten Client gesendet wird (ich habe die ID und das Verbindungsobjekt dieses Benutzers). Vielen Dank.
Vardan
1
@AsishAP, dies ist eine lange Diskussion ... Ich empfehle, dass Sie mit dem Lesen dieses Artikels beginnen , um sich zu orientieren, und dann SO nach anderen Fragen suchen, hier ist eine lesenswerte .
Myst
38

Hier ist ein einfacher Chat-Server für private / direkte Nachrichten.

package.json

{
  "name": "chat-server",
  "version": "0.0.1",
  "description": "WebSocket chat server",
  "dependencies": {
    "ws": "0.4.x"
  }
}

server.js

var webSocketServer = new (require('ws')).Server({port: (process.env.PORT || 5000)}),
    webSockets = {} // userID: webSocket

// CONNECT /:userID
// wscat -c ws://localhost:5000/1
webSocketServer.on('connection', function (webSocket) {
  var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
  webSockets[userID] = webSocket
  console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(webSockets))

  // Forward Message
  //
  // Receive               Example
  // [toUserID, text]      [2, "Hello, World!"]
  //
  // Send                  Example
  // [fromUserID, text]    [1, "Hello, World!"]
  webSocket.on('message', function(message) {
    console.log('received from ' + userID + ': ' + message)
    var messageArray = JSON.parse(message)
    var toUserWebSocket = webSockets[messageArray[0]]
    if (toUserWebSocket) {
      console.log('sent to ' + messageArray[0] + ': ' + JSON.stringify(messageArray))
      messageArray[0] = userID
      toUserWebSocket.send(JSON.stringify(messageArray))
    }
  })

  webSocket.on('close', function () {
    delete webSockets[userID]
    console.log('deleted: ' + userID)
  })
})

Anleitung

Führen Sie npm installzum Testen die Installation aus ws. Um den Chat-Server zu starten, führen Sie ihn auf einer Registerkarte des Terminals aus node server.js(oder npm start). Dann in einem anderen Terminal - Registerkarte laufen wscat -c ws://localhost:5000/1, wo 1ist die Verbindungs Benutzer-ID des Benutzers. Dann wird in einem dritten Terminal - Registerkarte laufen wscat -c ws://localhost:5000/2und dann eine Nachricht von Benutzer senden geben .21["1", "Hello, World!"]

Mängel

Dieser Chat-Server ist sehr einfach.

  • Beharrlichkeit

    Es werden keine Nachrichten in einer Datenbank wie PostgreSQL gespeichert. Daher muss der Benutzer, an den Sie eine Nachricht senden, mit dem Server verbunden sein, um sie zu empfangen. Andernfalls geht die Nachricht verloren.

  • Sicherheit

    Es ist unsicher.

    • Wenn ich die URL des Servers und die Benutzer-ID von Alice kenne, kann ich mich als Alice ausgeben, dh eine Verbindung zum Server als sie herstellen, sodass ich ihre neuen eingehenden Nachrichten empfangen und Nachrichten von ihr an jeden Benutzer senden kann, dessen Benutzer-ID ich ebenfalls kenne. Um die Sicherheit zu erhöhen, ändern Sie den Server so, dass er beim Herstellen einer Verbindung Ihr Zugriffstoken (anstelle Ihrer Benutzer-ID) akzeptiert. Anschließend kann der Server Ihre Benutzer-ID von Ihrem Zugriffstoken abrufen und Sie authentifizieren.

    • Ich bin nicht sicher, ob es eine WebSocket Secure ( wss://) -Verbindung unterstützt, da ich sie nur getestet habe localhost, und ich bin nicht sicher, wie ich eine sichere Verbindung herstellen soll localhost.

ma11hew28
quelle
Weiß jemand, wie man eine WebSocket Secure Localhost-Verbindung herstellt ?
ma11hew28
1
Hier gilt das gleiche. Wenn Sie jedoch den Teil 'upgradeReq ...' durch 'ws.upgradeReq.headers [' sec-websocket-key ']' ersetzen, funktioniert dies. (via github.com/websockets/ws/issues/310 )
dirkk0
1
Bestes Beispiel, um den WS in der Praxis zu starten! Danke
NeverEndingQueue
Tolles Beispiel! Ich habe in den letzten Tagen nach einer
solchen
5

Für Benutzer mit wsVersion 3 oder höher. Wenn Sie die Antwort von @ ma11hew28 verwenden möchten, ändern Sie diesen Block einfach wie folgt.

webSocketServer.on('connection', function (webSocket) {
  var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
webSocketServer.on('connection', function (webSocket, req) {
  var userID = parseInt(req.url.substr(1), 10)

ws Das Paket hat upgradeReq verschoben, um ein Objekt anzufordern, und Sie können den folgenden Link für weitere Details überprüfen.

Referenz: https://github.com/websockets/ws/issues/1114

Dylandy
quelle
1
Du hast mich vor viel Ärger bewahrt.
Hypothese
1

Ich möchte mitteilen, was ich getan habe. Hoffe, es verschwendet nicht deine Zeit.

Ich habe eine Datenbanktabelle mit Feld-ID, IP, Benutzername, Anmeldezeit und Abmeldezeit erstellt. Wenn sich ein Benutzer anmeldet, ist logintime unixtimestamp unix korrekt. Und wenn die Verbindung in der Websocket-Datenbank gestartet wird, wird nach der größten Anmeldezeit gesucht. Es wird kommen Benutzer angemeldet.

Und wenn sich der Benutzer abmeldet, wird die korrekte Abmeldezeit gespeichert. Der Benutzer wird zu dem, der die App verlassen hat.

Immer wenn eine neue Nachricht eingeht, werden Websocket-ID und IP verglichen und der zugehörige Benutzername wird angezeigt. Es folgen Beispielcode ...

// when a client connects
function wsOnOpen($clientID) {
      global $Server;
      $ip = long2ip( $Server->wsClients[$clientID][6] );

      require_once('config.php');
      require_once CLASSES . 'class.db.php';
      require_once CLASSES . 'class.log.php';

      $db = new database();
      $loga = new log($db);

      //Getting the last login person time and username
      $conditions = "WHERE which = 'login' ORDER BY id DESC LIMIT 0, 1";
      $logs = $loga->get_logs($conditions);
      foreach($logs as $rows) {

              $destination = $rows["user"];
              $idh = md5("$rows[user]".md5($rows["time"]));

              if ( $clientID > $rows["what"]) {
                      $conditions = "ip = '$ip', clientID = '$clientID'  

                      WHERE logintime = '$rows[time]'";
                      $loga->update_log($conditions);
             }
      }
      ...//rest of the things
} 
Tashi
quelle
0

interessanter Beitrag (ähnlich wie ich es mache). Wir erstellen eine API (in C #), um Spender mit WebSockets zu verbinden. Für jeden Spender erstellen wir ein ConcurrentDictionary, in dem WebSocket und DispenserId gespeichert sind, damit jeder Dispenser auf einfache Weise ein WebSocket erstellen und anschließend ohne Thread-Probleme verwenden kann (Aufruf bestimmter Funktionen) auf dem WebSocket wie GetSettings oder RequestTicket). Der Unterschied für Sie ist beispielsweise die Verwendung von ConcurrentDictionary anstelle eines Arrays zum Isolieren jedes Elements (dies wurde in Javascript nie versucht). Freundliche Grüße,

wolf354
quelle
Dies kann ein Kommentar und keine Antwort sein. Bitte lesen Sie die Regeln.
Papadopoulos