Heim  >  Artikel  >  Web-Frontend  >  Wie kann man in Node and React in Echtzeit kommunizieren?

Wie kann man in Node and React in Echtzeit kommunizieren?

青灯夜游
青灯夜游nach vorne
2020-09-12 10:55:064327Durchsuche

Wie kann man in Node and React in Echtzeit kommunizieren?

Empfohlene Tutorials: Node-JS-Tutorial, React-Tutorial, WebSocket-Tutorial

Web hat einen langen Weg zurückgelegt, um die Vollduplex- (oder bidirektionale) Kommunikation zwischen Clients und Servern zu unterstützen. Dies ist der Hauptzweck des WebSocket-Protokolls: die Bereitstellung einer dauerhaften Echtzeitkommunikation zwischen einem Client und einem Server über eine einzige TCP-Socket-Verbindung.

Das WebSocket-Protokoll hat nur zwei Ziele: 1) Öffnen des Handshakes, 2) Erleichtern der Datenübertragung. Sobald der Handshake zwischen Server und Client erfolgreich war, können sie Daten mit weniger Aufwand aneinander senden.

Die WebSocket-Kommunikation erfolgt über einen einzelnen TCP-Socket unter Verwendung des WS- (Port 80) oder WSS-Protokolls (Port 443). Laut Can I Use unterstützen zum Zeitpunkt des Schreibens fast alle Browser außer Opera Mini WebSockets.

Aktuelle Situation

Historisch gesehen erforderte die Erstellung von Webanwendungen, die Echtzeit-Datenkommunikation erfordern (z. B. Spiele oder Chat-Anwendungen), einen „Missbrauch des HTTP-Protokolls“ zur Einrichtung einer bidirektionalen Datenübertragung. Obwohl es viele Möglichkeiten gibt, Echtzeitfunktionen zu implementieren, ist keine so effizient wie WebSockets. HTTP-Polling, HTTP-Streaming, Comet, SSE – sie alle haben ihre Nachteile. HTTP-Abfrage

Der erste Versuch, das Problem zu lösen, besteht darin, den Server regelmäßig abzufragen. Der lange HTTP-Polling-Lebenszyklus ist wie folgt:

Der Client stellt eine Anfrage und wartet auf eine Antwort.
  1. Der Server verzögert die Reaktion, bis eine Änderung, ein Update oder eine Zeitüberschreitung auftritt. Die Anfrage bleibt „ausstehend“, bis der Server dem Client etwas zurückgibt.
  2. Wenn es auf der Serverseite einige Änderungen oder Aktualisierungen gibt, wird die Antwort an den Client zurückgesendet.
  3. Der Client sendet eine neue lange Abfrageanforderung, um auf die nächsten Änderungen zu warten.
  4. Es gibt viele Schwachstellen bei langen Abfragen – Header-Overhead, Latenz, Zeitüberschreitungen, Caching usw.

HTTP-Streaming

Dieser Mechanismus reduziert den Schmerz der Netzwerklatenz, da die ursprüngliche Anfrage auf unbestimmte Zeit offen bleibt. Selbst nachdem der Server die Daten übermittelt hat, wird die Anforderung nie beendet. Die ersten drei Schritte der Lebenszyklusmethode im HTTP-Fluss sind dieselben wie beim HTTP-Polling.

Wenn die Antwort jedoch an den Client zurückgesendet wird, wird die Anfrage nie beendet, der Server hält die Verbindung offen und sendet neue Updates, wenn Änderungen auftreten.

Server gesendete Ereignisse (SSE)

Mit SSE überträgt der Server Daten an den Client. Chat- oder Gaming-Anwendungen können sich nicht vollständig auf SSE verlassen. Der perfekte Anwendungsfall für SSE ist so etwas wie der Newsfeed von Facebook: Immer wenn neue Beiträge veröffentlicht werden, schiebt der Server sie auf die Timeline. SSE wird über herkömmliches HTTP gesendet und hat eine Begrenzung der Anzahl offener Verbindungen.

Diese Methoden sind nicht nur ineffizient, der Code zu ihrer Wartung ist auch für Entwickler ermüdend.

WebSocket

WebSockets sollen bestehende bidirektionale Kommunikationstechnologien ersetzen. Die oben beschriebenen bestehenden Methoden sind weder zuverlässig noch effizient, wenn es um Vollduplex-Echtzeitkommunikation geht.

WebSockets ähneln SSE, eignen sich aber auch hervorragend für die Weiterleitung von Nachrichten vom Client zurück an den Server. Da die Daten über eine einzige TCP-Socket-Verbindung bereitgestellt werden, sind Verbindungsbeschränkungen kein Problem mehr.

Praktisches Tutorial

Wie in der Einleitung erwähnt, hat das WebSocket-Protokoll nur zwei Ziele. Sehen wir uns an, wie WebSockets diese Agenden umsetzt. Dazu werde ich ein Profil eines Node.js-Servers erstellen und ihn mit einem mit React.js erstellten Client verbinden.

Agenda 1: WebSocket richtet einen Handshake zwischen Server und Client ein.

Erstellen eines Handshakes auf Serverebene.

Wir können einen einzelnen Port verwenden, um einen HTTP-Dienst bzw. einen WebSocket-Dienst bereitzustellen. Der folgende Code zeigt die Erstellung eines einfachen HTTP-Servers. Nach der Erstellung binden wir den WebSocket-Server an den HTTP-Port:

const webSocketsServerPort = 8000;
const webSocketServer = require('websocket').server;
const http = require('http');
// Spinning the http server and the websocket server.
const server = http.createServer();
server.listen(webSocketsServerPort);
const wsServer = new webSocketServer({
  httpServer: server
});

Nach der Erstellung des WebSocket-Servers müssen wir den Handshake akzeptieren, wenn wir eine Anfrage vom Client erhalten. Ich speichere alle verbundenen Clients als Objekte im Code und verwende die eindeutige Benutzer-ID, wenn ich Anfragen vom Browser erhalte.

// I'm maintaining all active connections in this object
const clients = {};

// This code generates unique userid for everyuser.
const getUniqueID = () => {
  const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  return s4() + s4() + '-' + s4();
};

wsServer.on('request', function(request) {
  var userID = getUniqueID();
  console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');
  // You can rewrite this part of the code to accept only the requests from allowed origin
  const connection = request.accept(null, request.origin);
  clients[userID] = connection;
  console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients))
});

Was passiert also, wenn eine Verbindung akzeptiert wird?

Beim Senden einer regulären HTTP-Anfrage zum Herstellen einer Verbindung sendet der Client im Anforderungsheader *Sec-WebSocket-Key*. Der Server kodiert und hasht diesen Wert und fügt die vordefinierte GUID hinzu. Es reagiert auf den in *Sec-WebSocket-Accept* generierten Wert im vom Server gesendeten Handshake.

Sobald die Anfrage auf dem Server akzeptiert wird (nach der erforderlichen Validierung), wird der Handshake mit dem Statuscode 101 abgeschlossen. Wenn Sie in Ihrem Browser etwas anderes als den Statuscode 101 sehen, bedeutet dies, dass das WebSocket-Upgrade fehlgeschlagen ist und die normale HTTP-Semantik befolgt wird. *Sec-WebSocket-Key*。服务器对此值进行编码和散列,并添加预定义的 GUID。它回应了服务器发送的握手中 *Sec-WebSocket-Accept*中生成的值。

一旦请求在服务器中被接受(在必要验证之后),就完成了握手,其状态代码为 101。如果在浏览器中看到除状态码 101

*Sec-WebSocket-Accept* 头字段指示服务器是否愿意接受连接。此外如果响应缺少 *Upgrade* 头字段,或者 *Upgrade* 不等于 websocket,则表示 WebSocket 连接失败。

成功的服务器握手如下所示:

HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols
Connection: Upgrade
Sec-WebSocket-Accept: Nn/XHq0wK1oO5RTtriEWwR4F7Zw=
Upgrade: websocket

在客户端级别创建握手

在客户端,我使用与服务器中的相同 WebSocket 包来建立与服务器的连接(Web IDL 中的 WebSocket API 正在由W3C 进行标准化)。一旦服务器接受请求,我们将会在浏览器控制台上看到 WebSocket Client Connected

这是创建与服务器的连接的初始脚手架:

import React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";

const client = new W3CWebSocket('ws://127.0.0.1:8000');

class App extends Component {
  componentWillMount() {
    client.onopen = () => {
      console.log('WebSocket Client Connected');
    };
    client.onmessage = (message) => {
      console.log(message);
    };
  }
  
  render() {
    return (
      <div>
        Practical Intro To WebSockets.
      </div>
    );
  }
}

export default App;

客户端发送以下标头来建立握手:

HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: vISxbQhM64Vzcr/CD7WHnw==
Origin: http://localhost:3000
Sec-WebSocket-Version: 13

现在客户端和服务器通过相互握手进行了连接,WebSocket 连接可以在接收消息时传输消息,从而实现 WebSocket 协议的第二个议程。

议程2:实时信息传输

Wie kann man in Node and React in Echtzeit kommunizieren?

我将编写一个基本的实时文档编辑器,用户可以将它们连接在一起并编辑文档。我跟踪了两个事件:

  1. 用户活动:每次用户加入或离开时,我都会将消息广播给所有连接其他的客户端。
  2. 内容更改:每次修改编辑器中的内容时,都会向所有连接的其他客户端广播。

该协议允许我们用二进制数据或 UTF-8 发送和接收消息(注意:传输和转换 UTF-8 的开销较小)。

只要我们对套接字事件onopenoncloseonmessage有了充分的了解,理解和实现 WebSockets 就非常简单。客户端和服务器端的术语相同。

在客户端发送和接收消息

在客户端,当新用户加入或内容更改时,我们用 client.send 向服务器发消息,以将新信息提供给服务器。

/* When a user joins, I notify the
server that a new user has joined to edit the document. */
logInUser = () => {
  const username = this.username.value;
  if (username.trim()) {
    const data = {
      username
    };
    this.setState({
      ...data
    }, () => {
      client.send(JSON.stringify({
        ...data,
        type: "userevent"
      }));
    });
  }
}

/* When content changes, we send the
current content of the editor to the server. */
onEditorStateChange = (text) => {
 client.send(JSON.stringify({
   type: "contentchange",
   username: this.state.username,
   content: text
 }));
};

我们跟踪的事件是:用户加入和内容更改。

从服务器接收消息非常简单:

componentWillMount() {
  client.onopen = () => {
   console.log(&#39;WebSocket Client Connected&#39;);
  };
  client.onmessage = (message) => {
    const dataFromServer = JSON.parse(message.data);
    const stateToChange = {};
    if (dataFromServer.type === "userevent") {
      stateToChange.currentUsers = Object.values(dataFromServer.data.users);
    } else if (dataFromServer.type === "contentchange") {
      stateToChange.text = dataFromServer.data.editorContent || contentDefaultMessage;
    }
    stateToChange.userActivity = dataFromServer.data.userActivity;
    this.setState({
      ...stateToChange
    });
  };
}

在服务器端发送和侦听消息

在服务器中,我们只需捕获传入的消息并将其广播到连接到 WebSocket 的所有客户端。这是臭名昭着的 Socket.IO 和 WebSocket 之间的差异之一:当我们使用 WebSockets 时,我们需要手动将消息发送给所有客户端。  Socket.IO 是一个成熟的库,所以它自己来处理。

const sendMessage = (json) => {
  // We are sending the current data to all connected clients
  Object.keys(clients).map((client) => {
    clients[client].sendUTF(json);
  });
}

connection.on(&#39;message&#39;, function(message) {
    if (message.type === &#39;utf8&#39;) {
      const dataFromClient = JSON.parse(message.utf8Data);
      const json = { type: dataFromClient.type };
      if (dataFromClient.type === typesDef.USER_EVENT) {
        users[userID] = dataFromClient;
        userActivity.push(`${dataFromClient.username} joined to edit the document`);
        json.data = { users, userActivity };
      } else if (dataFromClient.type === typesDef.CONTENT_CHANGE) {
        editorContent = dataFromClient.content;
        json.data = { editorContent, userActivity };
      }
      sendMessage(JSON.stringify(json));
    }
  });

将消息广播到所有连接的客户端。

Wie kann man in Node and React in Echtzeit kommunizieren?

浏览器关闭后会发生什么?

在这种情况下,WebSocket调用 close 事件,它允许我们编写终止当前用户连接的逻辑。在我的代码中,当用户离开文档时,会向其余用户广播消息:

connection.on(&#39;close&#39;, function(connection) {
    console.log((new Date()) + " Peer " + userID + " disconnected.");
    const json = { type: typesDef.USER_EVENT };
    userActivity.push(`${users[userID].username} left the document`);
    json.data = { users, userActivity };
    delete clients[userID];
    delete users[userID];
    sendMessage(JSON.stringify(json));
  });

该应用程序的源代码位于GitHub上的 repo 中。

结论

WebSockets 是在应用中实现实时功能的最有趣和最方便的方法之一。它为我们提供了能够充分利用全双工通信的灵活性。我强烈建议在尝试使用 Socket.IO 和其他可用库之前先试试 WebSockets。

编码快乐!

更多编程相关知识,请访问:编程教学!!

Das obige ist der detaillierte Inhalt vonWie kann man in Node and React in Echtzeit kommunizieren?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen