Node と React でリアルタイムに通信するにはどうすればよいですか?

青灯夜游
リリース: 2020-09-12 10:55:06
転載
4336 人が閲覧しました

Node と React でリアルタイムに通信するにはどうすればよいですか?

#チュートリアルの推奨事項:

node js チュートリアルReact チュートリアルWebSocket チュートリアル

Web は、クライアントとサーバー間の全二重 (または双方向) 通信をサポートするために長い道のりを歩んできました。これが WebSocket プロトコルの主な目的であり、単一の TCP ソケット接続を介してクライアントとサーバー間の永続的なリアルタイム通信を提供することです。

WebSocket プロトコルには 2 つのアジェンダしかありません。1) ハンドシェイクを開始すること、2) データ転送を支援することです。サーバーとクライアントのハンドシェイクが成功すると、ほとんどオーバーヘッドなく相互にデータを自由に送信できるようになります。

WebSocket 通信は、WS (ポート 80) または WSS (ポート 443) プロトコルを使用して、単一の TCP ソケット上で行われます。

Can I Use によると、この記事の執筆時点では、Opera Mini を除くほぼすべてのブラウザが WebSocket をサポートしています。

現在の状況

これまで、リアルタイム データ通信を必要とする Web アプリケーション (ゲームやチャット アプリケーションなど) を作成するには、

HTTP プロトコルを悪用して の 2 つを確立する必要がありました。データ転送の方法。リアルタイム機能を実装する方法は数多くありますが、WebSocket ほど効率的なものはありません。 HTTP ポーリング、HTTP ストリーミング、Comet、SSE - それらにはすべて欠点があります。

HTTP ポーリング

問題を解決する最初の試みは、サーバーを定期的にポーリングすることです。 HTTP ロングポーリングのライフサイクルは次のとおりです。

    クライアントはリクエストを作成し、応答を待ちます。
  1. サーバーは、変更、更新、またはタイムアウトが発生するまで応答を遅らせます。サーバーがクライアントに何かを返すまで、リクエストは「保留中」のままになります。
  2. サーバー側で何らかの変更または更新があると、サーバー側は応答をクライアントに送り返します。
  3. クライアントは、次の変更セットをリッスンするために新しいロング ポーリング リクエストを送信します。
ロングポーリングには、ヘッダーのオーバーヘッド、遅延、タイムアウト、キャッシュなど、多くの脆弱性があります。

HTTP ストリーミング

このメカニズムにより、最初のリクエストが無期限にオープンされたままになるため、ネットワーク遅延の苦痛が軽減されます。サーバーがデータをプッシュした後でも、リクエストは終了しません。 HTTP フローのライフサイクル メソッドの最初の 3 つのステップは、HTTP ポーリングの場合と同じです。

ただし、応答がクライアントに返されるとき、要求は決して終了せず、サーバーは接続を開いたままにして、変更が発生したときに新しい更新を送信します。

サーバー送信イベント (SSE)

SSE を使用して、サーバーはデータをクライアントにプッシュします。チャットまたはゲーム アプリケーションは SSE に完全に依存することはできません。 SSE の最適な使用例は、Facebook のニュース フィードのようなものです。新しい投稿が公開されるたびに、サーバーはそれをタイムラインにプッシュします。 SSE は従来の HTTP 経由で送信され、オープン接続の数には制限があります。

これらの方法は非効率であるだけでなく、それらを保守するためのコードも開発者にとって面倒なものです。

WebSocket

WebSocket は、既存の双方向通信テクノロジを置き換えるように設計されています。上で説明した既存の方法は、全二重リアルタイム通信に関しては信頼性も効率性もありません。

WebSocket は SSE に似ていますが、クライアントからサーバーにメッセージを戻すのにも優れています。データは単一の TCP ソケット接続を介して提供されるため、接続の制限は問題になりません。


実践的なチュートリアル

冒頭で述べたように、WebSocket プロトコルには 2 つのアジェンダしかありません。 WebSocket がこれらのアジェンダをどのように実装するかを見てみましょう。これを行うために、Node.js サーバーをプロファイリングし、React.js で構築されたクライアントに接続します。

議題 1: WebSocket はサーバーとクライアント間のハンドシェイクを確立します

サーバー レベルでのハンドシェイクの作成

単一のポートを使用して、HTTP サービスと WebSocket サービスをそれぞれ提供できます。以下のコードは、単純な HTTP サーバーの作成を示しています。作成したら、WebSocket サーバーを HTTP ポートにバインドします。

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
});
ログイン後にコピー

WebSocket サーバーを作成した後、クライアントからリクエストを受信するときにハンドシェイクを受け入れる必要があります。接続されているすべてのクライアントをコード内のオブジェクトとして保存し、ブラウザーからリクエストを受信するときに一意のユーザー ID を使用します。

// 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))
});
ログイン後にコピー

それでは、接続が受け入れられると何が起こるでしょうか?

接続を確立するために通常の HTTP リクエストを送信するとき、クライアントはリクエスト ヘッダーで

*Sec-WebSocket-Key* を送信します。サーバーはこの値をエンコードしてハッシュし、事前定義された GUID を追加します。これは、サーバーによって送信されたハンドシェイクの *Sec-WebSocket-Accept* で生成された値で応答します。

リクエストがサーバーで受け入れられると (必要な検証の後)、ハンドシェイクはステータス コード

101 で完了します。ブラウザにステータス コード 101 以外のものが表示された場合は、WebSocket のアップグレードが失敗し、通常の HTTP セマンティクスが適用されることを意味します。

*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:实时信息传输

Node と React でリアルタイムに通信するにはどうすればよいですか?

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

  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));
    }
  });
ログイン後にコピー

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

Node と React でリアルタイムに通信するにはどうすればよいですか?

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

在这种情况下,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。

编码快乐!

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

以上がNode と React でリアルタイムに通信するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
関連するチュートリアル
人気のおすすめ
最新のコース
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート