JS WebSocketを使用して簡単なチャットを実装する方法

JS WebSocketを使用して簡単なチャットを実装する方法

ショートポーリング

ショートポーリングの実装アイデアは、ブラウザが数秒ごとにサーバーに HTTP リクエストを送信するというものです。リクエストを受信した後、サーバーはデータの更新の有無に関係なく直接応答します。サーバーが応答を完了すると、TCP 接続が閉じられます。コードの実装も最もシンプルで、XHR を使用して setInterval を通じてバックエンドに定期的にリクエストを送信し、最新のデータを取得します。

setInterval(関数() {
  フェッチ(url).then((res) => {
      // 成功コード
  })
}, 3000);

利点: 実装が簡単。

デメリット: データが短期間同期されなくなり、無効なリクエストが大量に発生し、セキュリティが低下し、リソースが無駄になります。

ロングポーリング

クライアントがリクエストを送信した後、サーバーはすぐにデータを返しません。サーバーはリクエスト接続をブロックし、サーバーのデータが更新されるか接続がタイムアウトするまですぐには切断しません。その後、クライアントは再度リクエストを送信して新しい接続を作成し、このプロセスを繰り返して最新のデータを取得します。一般的な効果は次のとおりです。

クライアントコードは次のとおりです。

関数async() {
    フェッチ(url).then((res) => {
        非同期();
        // 成功コード
    }).catch(() => {
        // タイムアウト async();
    })
}

利点: ポーリングに比べて最適化されており、適時性が向上します。

デメリット: 接続を中断したままにするとリソースが消費され、サーバーは有効なデータを返さず、プログラムはタイムアウトになります。

ウェブソケット

上記のショート ポーリングとロング ポーリングはどちらも、通信を実行する前にクライアントが Ajax リクエストを開始する必要があります。これらは HTTP プロトコルを使用し、サーバーはクライアントに情報を積極的にプッシュすることはできません。

スポーツイベント、チャットルーム、リアルタイムの位置情報などのシナリオでは、サーバーに接続するためにリクエストを継続的に送信する必要があるため、ポーリングは非常に非効率になり、リソースを浪費します。 WebSocket の登場により、サーバーがクライアントに情報を積極的に送信できるようになり、ブラウザはリアルタイムで通信できるようになりました。

WebSocket を使用したことがない人は、それが高度な技術だと考えるかもしれません。実は、WebSocket はよく使われる API が少なく、習得しやすいのが特長です。しかし、使い方を紹介する前に、まずは通信原理について見てみましょう。

コミュニケーションの原則

クライアントがサーバーとの WebSocket 接続を確立する場合、クライアントとサーバー間のハンドシェイク プロセス中に、クライアントはまず、WebSocket 接続を確立したいことをサーバーに通知する Upgrade 要求ヘッダーを含む HTTP 要求をサーバーに送信します。

クライアント側で WebSocket 接続を確立するのは非常に簡単です。

ws = new WebSocket('ws://localhost:9000'); を作成します。

HTTP や HTTPS と同様に、ws にもそれに対応する wss があり、安全な接続を確立するために使用されます。ローカルでは ws を例として取り上げました。このときのリクエスト ヘッダーは次のとおりです。

受け入れエンコーディング: gzip、deflate、br
受け入れ言語: zh-CN,zh;q=0.9
キャッシュ制御: キャッシュなし
: : : : : : : : : : : : : : :
ホスト: localhost:9000
オリジン: http://localhost:9000
プラグマ: キャッシュなし
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: 5fTJ1LTuh3RKjsJxydyifQ== // 応答ヘッダーに対応します Sec-WebSocket-Accept Sec-WebSocket-Version: 13 // websocket プロトコルのバージョンを示します Upgrade: websocket // websocket プロトコルへのアップグレードを示します User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/76.0.3809.132 Safari/537.36

応答ヘッダーは次のとおりです。

接続: アップグレード
Sec-WebSocket-Accept: ZUip34t+bCjhkvxxwhmdEOyx9hE=
アップグレード: websocket 

このとき、応答行 (General) でステータス コードが 101 Switching Protocols であることがわかり、接続が HTTP プロトコルから WebSocket 通信プロトコルに変換されたことがわかります。 変換が成功すると、接続は中断されずに全二重通信が確立され、その後のメッセージの送受信はこの接続チャネルを介して行われます。

リクエスト ヘッダーには Sec-WebSocket-Key フィールドがあり、これは対応するヘッダーの Sec-WebSocket-Accept に対応していることに注意してください。その機能は、悪意のある接続や無効な接続などの基本的な保護を提供することです。 Sec-WebSocket-Key は、クライアントによってランダムに生成される base64 エンコーディングです。サーバーはこのエンコーディングを使用し、固定アルゴリズムに従います。

GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // 固定文字列 accept = base64(sha1(key + GUID)); // key は Sec-WebSocket-Key 値、accept は Sec-WebSocket-Accept 値

GUID 文字列は RFC6455 で正式に定義された固定文字列であり、変更することはできません。

クライアントはサーバーから Sec-WebSocket-Accept 応答を受信すると、同じアルゴリズムを使用して、以前に生成した Sec-WebSocket-Key を計算します。一致した場合、ハンドシェイクは成功します。次に、HTTP 応答ステータス コードが 101 (プロトコルの切り替え) であるかどうかを判断します。そうであれば、接続を確立すれば完了です。

シンプルな1対1チャットの実装

次に、純粋なテキスト メッセージ タイプの 1 対 1 チャット (シングル チャット) 機能を実装します。これ以上は何もせずに、コードに直接進み、コメントに注目してみましょう。

クライアント:

関数 connectWebsocket() {
    ws = 新しい WebSocket ('ws://localhost:9000');
    // 接続成功を待機する ws.onopen = () => {
        console.log('サーバーWebSocketへの接続が成功しました');
        ws.send(JSON.stringify(msgData)); // send メソッドはメッセージをサーバーに送信します};

    // サーバーメッセージをリッスンする(メッセージを受信する)
    ws.onmessage = (メッセージ) => {
        message = JSON.parse(msg.data); とします。
        console.log('受信したメッセージ:', message)
        elUl.innerhtml += `<li>小秋: ${message.content}</li>`;
    };

    // 接続失敗をリッスンする ws.onerror = () => {
        console.log('接続に失敗しました。再接続しています...');
        Websocket() を接続します。
    };

    // 接続の終了を待機する ws.onclose = () => {
        console.log('接続が閉じられました');
    };
};
Websocket を接続します。

上記から、WebSocket インスタンスの API は理解しやすく、シンプルで使いやすいことがわかります。メッセージは send() メソッドを通じて送信でき、onmessage イベントを使用してメッセージを受信し、その後メッセージが処理されてページに表示されます。 onerror イベント (リスニング接続の失敗) がトリガーされた場合は、接続が中断されないように再接続を実行するのが最適です。

サーバー ノード: (ここでは ws ライブラリを使用)

定数パス = require('path');
定数 express = require('express');
express() は、定数です。
const server = require('http').Server(app);
WebSocket は 'ws' を必要とします。

wss = new WebSocket.Server({ server: server });

wss.on('接続', (ws) => { 

  // クライアントからのメッセージをリッスンします ws.on('message', (message) => {
    コンソールログ(wss.clients.size);
    msgData = JSON.parse(メッセージ) とします。   
    msgData.type === 'open'の場合{
      // 初期接続時にセッションを識別します ws.sessionId = `${msgData.fromUserId}-${msgData.toUserId}`;
    } それ以外 {
      sessionId = `${msgData.toUserId}-${msgData.fromUserId}` とします。
      wss.clients.forEach(クライアント => {
        (クライアントのセッションID === セッションID) の場合 {
          client.send(message); //対応するクライアント接続にメッセージを送信します}
      })  
    }
  })

  // 接続が閉じられました ws.on('close', () => {
    console.log('接続が閉じられました');  
  });
});

同様に、サーバーにも対応する送信メソッドと受信メソッドがあります。完全なサンプルコードはここにあります

この方法では、ブラウザとサーバーは問題なくメッセージを送信でき、その効果は次のようになります。

緑の矢印は送信されたメッセージを表し、赤の矢印は受信されたメッセージを表します。

ハートビートキープアライブ

実際の WebSocket の使用においては、長時間通信が行われない場合、接続が不安定になることがあります。このような不明な状況によって発生する接続中断は、クライアントとサーバー間の通信に影響を与えます。

このような状況を防ぐために、ハートビート キープアライブ方式があります。クライアントはハートビートのように一定の間隔で ping を送信して、サーバーにまだ生きていることを伝え、サーバーも pong を返して、クライアントにサーバーがまだ生きていることを伝えます。 Ping/pong は実際にはビジネスとは何の関係もない偽のメッセージであり、ハートビート パケットとも呼ばれます。

接続が成功した後、60 秒などの固定間隔でハートビート パケットを送信できます。

間隔を設定する(() => {
    ws.send('これはハートビートメッセージです');
}, 60000)

要約する

上記の紹介を通じて、WebSocket についてある程度理解できたはずです。実際、WebSocket は神秘的なものではありません。ここでは、記事の内容を簡単にまとめます。 WebSocket インスタンスが作成されると、リクエスト メッセージに特別なフィールド Upgrade を含む HTTP リクエストが送信されます。次に、接続が HTTP プロトコルから WebSocket プロトコルに変換され、クライアントとサーバーの間で全二重通信が確立されます。この通信接続を通じて、WebSocket の send メソッドと onmessage イベントを使用して情報を交換できます。

以上が、JS WebSocket を使って簡単なチャットを実装する方法の詳細です。WebSocket の詳細については、123WORDPRESS.COM の他の関連記事にも注目してください。

以下もご興味があるかもしれません:
  • js シンプルなネットワーク速度テスト方法の完全な例
  • Baidu と Google のスピードテストの JavaScript コードにアクセスする
  • JS 非同期コードユニットテストの魔法 Promise
  • ネイティブ js はフォームの定期的な検証を実装します (検証後にのみ送信)
  • JS での Reduce Fold Unfold の使用法の詳細な説明
  • エレガントなJSコードの書き方
  • Vue での weixin-js-sdk の一般的な使用方法の詳細な説明
  • JSホモロジー戦略とCSRFの詳細な説明
  • JavaScript でネットワーク速度をテストする方法

<<:  MySQL のロードバランサーとして nginx を使用する方法

>>:  MySQL ビューの原則と使用例の概要

推薦する

Docker イメージ + nginx を使用して Vue プロジェクトをデプロイする方法

1. Vueプロジェクトのパッケージ化開発されたvueプロジェクトに次の名前を入力し、パッケージ化し...

Elementはスクリプトを使用して新しいコンポーネントを自動的に構築します

目次背景element-ui の自動構築はどのように機能しますか?メイクファイル新しい.jsファイル...

Vue ページレンダリングにおけるキーの適用例チュートリアル

導入フロントエンドプロジェクトの開発プロセスでは、el-table によって表示される結果列がコンポ...

TypeScript マッピング型の詳細

目次1. マップされた型2. マッピング修飾子3. キーの再マッピング4. さらなる探究序文: Ty...

HTMLタグのデフォルトスタイルの配置

html、address、blockquote、body、dd、div、dl、dt、fieldset...

MySQL での IN データボリュームの使用の最適化された記録

MySQL のバージョン番号は 5.7.28 です。テーブル A には 390 万件のレコードがあり...

JavaScriptはフォームデータの非同期取得を実装します

この記事では、フォームデータの非同期取得を実現するためのJavaScriptの具体的なコードを例とし...

type=fileファイル変更フォームの名前が正常にエコーされない問題を解決

easyui フレームワークのコードは次のとおりです。 css: .ファイルボックス{ フロート:...

MySQL 永続統計の詳細な説明

1. 永続的な統計情報の重要性:統計は、MySQL が実行プランを生成するためのガイドとして使用され...

MySQL の innodb_flush_log_at_trx_commit と sync_binlog を区別する方法

2 つのパラメータ innodb_flush_log_at_trx_commit と sync_bi...

JavaScript における URL オブジェクトの素晴らしい使い方

目次序文解析パラメータURLパラメータを変更する要約する序文URLオブジェクトはページ側ではあまり使...

Vueはログイン認証コードを実装する

この記事では、ログイン認証コードを実装するためのvueの具体的なコードを例として紹介します。具体的な...

Docker の Windows ストレージ パス設定操作

Windows 10 に Docker をインストールする場合、コンテナタイプを Linux コンテ...

Nginx+tomcat ロードバランシングクラスタの実装方法

実験環境は以下のとおりですここでは、4 台のサーバー (1 台の nginx、負荷用の 2 台の t...

Linuxロスレス展開方法

概要クラウド プラットフォームのお客様のサーバーでは、業務量が拡大し続けるとディスク容量が不足する場...