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 ビューの原則と使用例の概要

推薦する

JS ES6コーディング標準の詳細な説明

目次1. ブロックスコープ1.1. let は var を置き換える1.2. グローバル定数とスレッ...

React Router 5.1.0 はページジャンプナビゲーションを実装するために useHistory を使用します

目次1. withRouterコンポーネントを使用する2. ルートタグを使用するReactRoute...

一般的な HTTP ステータス コード 10 個の詳細な説明

HTTP ステータス コードは、Web サーバーの HTTP 応答ステータスを示すために使用される ...

MySQLクエリで大文字と小文字を区別しない問題を解決する方法

質問最近、SSH フレームワークを使用して実用的なプロジェクトを完了していたときに、長い間悩まされて...

MySQLデータストレージプロセスパラメータの詳細な例

MySQL ストアド プロシージャ パラメータには、in、out、inout の 3 種類があります...

Docker Tomcat のアクセス インターフェイスが表示されないのはなぜですか?

質問:オリジン サーバーはターゲット リソースの表現を見つけることができないか、既存の表現を公開した...

反応ループデータの実装(リスト)

まず、バックグラウンドから来るデータをシミュレートしてみましょう。ここでは、コードをわかりやすくする...

ハードコーディングに別れを告げ、フロントエンドテーブルがインスタンスコードを自動的に計算できるようにします。

序文私のチームが税制モジュールを開発していたとき、計算問題、特にグリッド内の計算を解決するために時間...

jsは多次元配列を1次元配列に変換し、それを並べ替えます

目次まず多次元配列の平坦化についてお話しましょう方法 1: flat()方法 2: 空の文字列を連結...

vue で h5 側のアプリを開きます (Android か Apple かを判断します)

1. 開発環境 vue+vant 2. コンピュータシステム Windows 10 Profess...

ウェブページで CSS スタイルを適用するさまざまな形式の概要

1. インライン スタイル (<body></body> 内に配置されます)...

Vue はファジークエリを実装します - MySQL データベースデータ

目次1. 需要2. 実装3. 結果1. 需要入力ボックスにデータを入力し、入力結果に基づいてデータベ...

MAC での MYSQL5.7.17 接続失敗の問題と解決策

MYSQL5.7.17 が MAC で接続できない問題。SQLBench_community 6.3...

HTML 基本ノート (推奨)

1. ウェブページの基本構造: XML/HTML コードコンテンツをクリップボードにコピー<...

iframe を使用して Web ページに天気の影響を表示します

CS: ...コードをコピーコードは次のとおりです。 *{マージン:0;パディング:0;リストスタイ...