JS で Websocket ベースのマルチターミナル ブリッジング プラットフォームを実装する方法

JS で Websocket ベースのマルチターミナル ブリッジング プラットフォームを実装する方法

1. デバッグ対象

主に、何をデバッグするのか、最終結果はどうなるのかを知る必要があります。

1. インターフェースをデバッグし、インターフェース アドレスを入力して、対応する結果を取得します。複数のデバイスを同時にデバッグできます。

2. jsapi をデバッグし、対応するメソッドを入力すると、ニュース クライアントに効果が表示されます。

デバッグ インターフェースに関しては、実際に簡単にデバッグする方法がありますが、Android システムとベータ クライアントという 2 つの制限があるため、Chrome ブラウザを介してブリッジします。ただし、この方法は iOS システムおよびクライアントの公式バージョンでは効果がありません。

2. WebSocketの機能

WebSocket プロトコルの最大の特徴は、サーバーがクライアントに情報を積極的にプッシュでき、クライアントもサーバーに情報を積極的に送信できることです。これは真の双方向の平等な対話であり、サーバー プッシュ テクノロジの一種です。

その他の機能は次のとおりです:

1. TCP プロトコル上に構築されているため、サーバー側の実装は比較的簡単です。

2. HTTP プロトコルとの互換性が良好です。デフォルトのポートも 80 と 443 であり、ハンドシェイク フェーズでは HTTP プロトコルが使用されるため、ハンドシェイク中にブロックされることは少なく、さまざまな HTTP プロキシ サーバーを通過できます。

3. データ形式は比較的軽量で、パフォーマンスのオーバーヘッドが少なく、通信が効率的です。

4. テキストデータとバイナリデータの両方を送信できます。

5. 同一オリジンの制限がないため、クライアントはどのサーバーとも通信できます。

6. プロトコル識別子は ws (暗号化されている場合は wss) であり、サーバー URL は URL です。

3. ソケット接続を確立する

パート 1 で設定したデバッグ目標を達成するために、ここで実装する必要がある関数は次のとおりです。

1. PC は部屋の所有者に相当します。部屋が作成されると、他のデバイスが部屋に入ることができます。1 台のデバイスは 1 つの部屋にしか入室できません。

2. クライアントには切断再接続メカニズムがあります。クライアントが切断された場合、再接続を試みることができます。

3. サーバーはハートビート検出メカニズムを維持します。新しいデバイスが入室したり、以前のデバイスが退室したりすると、現在の部屋のデバイス リストがタイムリーに更新されます。

3.1 部屋の作成方法

ブラウザにルーム ID を入力します。ブラウザがサーバーとの Websocket 接続を正常に確立すると、対応する QR コードがブラウザに作成されます。 WeChat/モバイルQQまたはQRコードをスキャンする他のデバイスでスキャンすると、事前に設定されたスキームプロトコルを通じてニュースクライアントの対応するデバッグページにジャンプできます。

クライアントがサーバーとの Websocket 接続を正常に確立すると、ルームへの入室に成功したことになり、対応するアイコンが PC 上に表示されます。

ws.open(サーバーID)
    .then(() => {
        // PC が正常に接続を確立した後 setStatus("linked"); // ページのステータスを更新 // QR コードを生成します qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
            url を設定します。
        });
    })
    .catch(e => {
        // 接続を確立できませんでした console.error(e);
        Modal.error({ title: "現在のサーバーに問題があり、修復中です" });
        setStatus("リンクを解除");
    });

3.2 クライアント切断再発メカニズム

モバイル端末のページには、画面が真っ暗になった場合など、何らかの理由でクライアントがソケット接続を自動的に切断する機能があります。

デバッグを容易にするために、切断後に毎回手動でクリックしたり、ページに再度アクセスしたりする必要はありません。ここでは、単純な切断と再接続のメカニズムを実装しました。 Websocket 接続が切断されると、onclose コールバックが実行されます。したがって、onclose イベントに再接続メカニズムを実装できます。

同時に、無制限の再接続試行を防ぐために、ここでも制限を設定しました。再接続の最大回数は 3 回です。3 回後に再接続されない場合は接続が停止され、再接続が成功すると再接続回数は 3 にリセットされます。

切断する場合:

// 切断時 ws.onclose(() => {
    タイマー = setTimeout(() => {
        setStatus("リンクを解除");
        コードURLを設定します。
    }, 500);

    再接続番号--;
    // 再接続回数を制限する if (reconnectNum >= 0) {
        _open(); //再接続を試みる}
});

接続が成功すると:

ws.open(serverId).then(() => {
    // PC が正常に接続を確立した後 + reconnectNum = 3;
    +タイマー&& clearTimeout(タイマー);

    setStatus("linked"); // ページのステータスを更新 // QR コードを生成します qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
        url を設定します。
    });
});

3.3 ハートビート検出

QQ グループでチャットするときと同じように、誰がオンラインであるかは一目でわかります。誰かがチャット グループに参加したり、誰かが退出したりした場合は、ホストに通知し、グループ リストをタイムリーに更新する必要があります。

ハートビート検出には、クライアントによって開始されるハートビート検出とサーバーによって維持されるハートビート検出の 2 つの主なタイプがあります。次の 2 つのタイプを見てみましょう。

1. クライアントによって開始されたハートビート: 一定の間隔で、ping データがサーバーに送信されます。通常の状況では、サーバーはクライアントに pong を返します。クライアントが onmessage イベントを通じてこれを監視できる場合、リクエストが正常であることを意味します。

2. サーバーによって維持されるハートビート: 定期的にすべての接続のステータスを確認します。ステータスが切断されている場合は、リストから削除します。

ここでは、サーバーによって維持されるハートビート検出を使用します。部屋内のデバイスの数が変わると、サーバーは最新のデバイス リストをクライアントにプッシュします。

// クライアントの接続状態を継続的に監視します // 接続が切断された場合は、クライアントをクリアします let aliveClients = new Map();
lastAliveLength = new Map();
間隔を設定する(() => {
    クライアントを {} にします。
    wss.clients.forEach(関数each(ws) {
        ws.isAlive が false の場合
            ws.terminate() を返します。
        }
        定数 serverId = ws.serverId;
        if (クライアント[サーバーID]) {
            クライアント[サーバーID].push(ws);
        } それ以外 {
            クライアント[サーバーID] = [ws];
        }

        ws.isAlive = false;
        ws.ping(() => {});
    });
    for (let serverId in クライアント) {
        aliveClients.set(serverId, クライアント[serverId]);
        const 長さ = クライアント[サーバーID].長さ;

        // 現在の serverId に接続されているデバイスの数が変わった場合は、メッセージを送信します if (length !== lastAliveLength.get(serverId)) {
            // 現在の serverId を持つすべてのデバイスにメッセージを送信します。sendAll("devices", client[serverId], serverId);

            //現在の serverId の最後の接続数を保存します。lastAliveLength.set(serverId, length);
        }
    }

    定数サイズ = wss.clients.size;

    console.log("接続番号: ", サイズ, 新しい Date().toTimeString());
}, 2000);

4. インターフェースをデバッグする

セクション 3 では PC とニュース クライアントを正常に接続しましたが、両端の間でデータをどのように通信するのでしょうか。

4.1 インターフェースのデバッグ

ここでは 3 つのフィールドを渡します:

1.serverId: 部屋番号。サーバーは、serverId を持つすべてのメンバーに情報をブロードキャストする必要があります。

2.type: この命令が何を実行するかを入力します。

3.msg: 受信パラメータ。

インターフェースのデバッグ中に渡されるパラメータは次のとおりです。

定数パラメータ = {
    type: "post", // タイプmsg: {
        // パラメータ URL: "https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336"
    }
};

クライアントがインターフェース要求を正常に完了すると、インターフェース結果、Cookie、デバイス情報が PC に返されます。

// リクエストメソッド const post = url => {
    if (window.TencentNews && window.TencentNews.post) {
        window.TencentNews.post(url, {}, window[id], { loginType: "qqorweixin" }, {});
    } それ以外の場合 (window.TencentNews && window.TencentNews.postData) {
        window.TencentNews.postData(url, '{"a":"b"}', id, "requestErrorCallback");
    }
};

// モバイル端末からサーバーにデータが送信されました ws.send({
    type: "postCb", // 実行結果メッセージ: {
        メソッド: "post",
        結果、
        クッキー: document.cookie,
        アプリ情報
    }
});

このようにして、フロントエンドに結果を表示することができ、それが実際のデータ要求になります。

4.2 履歴記録の保存

歴史的記録に関して言えば、私たちの周りの同級生たちは、裁判の過程で依然としてそれを非常に緊急に必要としています。そうしないと、以前のインターフェース アドレスをテストするたびに、それを再入力するか貼り付ける必要があり、非常に不便です。

ユーザーが要求した URL、返された結果、Cookie、デバイス情報などの比較的完全な情報をボスに保存し、履歴 URL のみをローカルに保存します。ユーザーが以前のインターフェイスを再度テストする必要がある場合は、それをクリックするだけです。以前にデバッグしたインターフェースを表示する必要がある場合は、Hawkeye で確認できます。

ローカルストレージは localStorage を使用して行われます。さらに重要なのは、mobx のレスポンシブ ツールも使用しているため、ユーザーがリクエストを完了するとすぐに、サイドの履歴ログで結果を確認できることです。

5. ニュースクライアントでのjsapiのデバッグ

インターフェースのデバッグに加えて、一部のニュース クライアントで jsapi をデバッグすることもできます。ニュース クライアントの jsapi を呼び出す方法は 2 つあります。

// 直接 window.TencentNews.login("qqorweixin", isLogined => console.log(isLogined)); を呼び出します。

// window.TencentNews.invoke("login", "qqorweixin", isLogined => console.log(isLogined)); を呼び出します。

ここでは、invoke メソッドを使用して jsapi を呼び出すことを選択しました。

PC は jsapi 呼び出しを開始します。

ws.send({
    タイプ: "call",
    メッセージ: {
        方法: 方法、
        パラメータ: slice.call(引数)
    }
});

モバイル端末はサーバーからのリクエストを受信すると、jsapi を呼び出し、実行結果を PC 端末に返します。

const handleNewsApi = async (msg: any): Promise<any> => {
    tencentReady() を待機します。

    const { メソッド、パラメータ } = msg;
    新しいPromiseを返します(resolve => {
        window.TencentNews.invoke(メソッド、...パラメータ、(結果: 任意) => {
            解決({ メソッド、結果 });
        });
    });
};

6. 結論

この時点で、私の「Websocket ベースのマルチターミナル ブリッジング プラットフォーム」は基本的に構築されました。ただし、簡単に説明する必要がある問題がまだ 2 つあります。

6.1 なぜ serverId を手動で入力する必要があるのですか?

最初は、ユーザーがルームを作成すると、システムがランダムに uuid を生成すると考えていましたが、ユーザーがページを更新すると uuid が変わってしまい、以前の uuid に接続できなくなるのではないかと考え、ここで手動入力に切り替えました。

6.2 クライアントからのすべてのソケット要求が同じプロセスに入るようにする方法

バックグラウンドで複数のプロセスを使用する場合、ユーザーのリクエストに介入しないと、リクエストへのランダムアクセスが発生し、400 のリクエストが生成されます。結局、最初の接続はプロセス A にあり、リクエストはプロセス B に送信され、プロセス B はそれをどのように処理するかがわかりません。

これを処理するには複数の方法があります。

方法導入アドバンテージ欠点
一貫性のあるハッシュアルゴリズムすべてのホストと接続は0から2^32-1までの仮想円に割り当てられます。 1. 大規模なアプリケーションに適用可能。
2. ホストまたはプロセスがクラッシュしても影響は小さい
実装がより複雑
nginx ディストリビューション組み込みの ip_hash は負荷分散を実現できます。
同じIPが固定バックエンドサーバーに割り当てられます
簡単な設定プロセスに集中する可能性がある

ここで使用するプラットフォームは、内部デバッグ プラットフォームです。ユーザー数は多くないため、ナットを割るのに大ハンマーを使用する必要はありません。また、マシンは 1 台しかないため、同じ IP が同じプロセスに入ることを考慮します。ここでは、nginx の ip_hash のアイデアを借用します。リクエストがメイン プロセスに到達すると、IP に対して重み付け計算を実行し、プロセスの数に応じて係数を取得します。

もちろん、この方法ではプロセス内のソケット接続が多すぎるという問題もありますが、ユーザー数が多くない場合はまったく問題ありません (この問題に対して、ウォーターフォール フロー メソッドなどの他の方法も検討しました。子プロセスに接続が割り当てられるたびに、まず接続数が最も少ないプロセスが取得され、次にこのプロセスに接続が割り当てられます。ただし、そのたびにテーブルを維持して計算する必要があります)。

6.3 複数プロセス間の通信

同じ部屋で、PC側のソケット接続と複数のモバイル端末の接続が同じプロセスで行われない場合、クロスプロセス問題が発生します。極端な例として、各ソケット接続は異なるプロセスにあるため、クライアントにリクエストを送信する必要があることを他のプロセスに通知する方法を検討する必要があります。

私たちのメカニズムを使用する比較的簡単な方法は、各 PC ユーザーがホストとなり、ルームを作成できるようにすることです。モバイル デバイスはルームのメンバーになります。各ルームは独立しており、互いに干渉しません。この方法では、部屋内のすべてのソケット接続を部屋識別子を通じて同じプロセスに配置するため、プロセス間の問題は発生しません。しかし、このアプローチの問題点は、部屋内の接続が多すぎると、他のプロセスがアイドル状態になっている間に、同じプロセスですべての接続を処理する必要があることです。

また、redis を使用することもできます。redis のパブリッシュ/サブスクライブ モードを使用して、現在のプロセス内のルーム ID と情報を他のプロセスにブロードキャストします。他のプロセスは同じルーム ID でソケット接続を持ち、対応する操作を実行します。

上記は、JS が websocket に基づいてマルチターミナル ブリッジング プラットフォームを実装する方法の詳細です。JS の websocket に基づくマルチターミナル ブリッジング プラットフォームの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Js パラメータ RSA 暗号化送信に jsencrypt.js を使用する
  • JavaScript を使用して BLOB 暗号化ビデオ ソース アドレスを実装する方法
  • JSはWebSocketロングポーリングリアルタイムメッセージプロンプトの効果を実装します
  • JavaScript WebSocketテクノロジーの詳細な説明
  • Websocket は JS 暗号化の例とアイデアの原則を直接バイパスします

<<:  MySQL 5.6.23 のインストールと設定環境変数のチュートリアル

>>:  Centos7 DockerでNginxファイルを変更するプロセスの詳細な説明

推薦する

フェッチネットワークリクエストのカプセル化例の詳細な説明

エクスポートデフォルト({ URL、 メソッド = 'GET'、 データ = nu...

Robots.txtの詳細な紹介

robots.txt の基本的な紹介Robots.txt はプレーンテキスト ファイルであり、Web...

CSS フレックスベースのテキストオーバーフロー問題の解決方法

重要でないflex-basisテキストオーバーフローに省略記号を追加するという小さな機能に多くの問題...

Linux で 1 回限りのスケジュールされたタスクを実行するための at コマンドの使用に関する詳細な説明

目次序文1. 一度限りの計画タスクの紹介2. コマンド3. 1回限りのスケジュールタスクを作成する4...

MySQL スローログ実践のまとめ

遅いログクエリ機能スロー ログ クエリの主な機能は、設定された時間しきい値を超える SQL ステート...

Linux ファイル記述子、ファイルポインタ、および inode の詳細

目次Linux - ファイル記述子、ファイルポインタ、インデックスノード1. Linux - ファイ...

VMware Esxi のルート パスワードを忘れた後に正常に取得する方法

CentOS6 インストール ディスク (任意のバージョン) を準備するか、別の pnux インスト...

一般的な Linux の問題に対する解決策の概要

1. VMwareでCentos7を接続し、固定IPを設定する1) まず、仮想イメージ名を右クリック...

MySQL の nvl() 関数に似た ifnull() 関数についての簡単な説明

IFNULL(式1,式2) expr1 が NULL でない場合、IFNULL() は expr1 ...

Linuxコマンドに基づいてフォルダー内の特定のファイルパスを抽出します

最近では、特定のフォルダ内の特定のファイルを自動的に検索する必要があり、ファイルパスとファイル名を別...

vue3.0+vant3.0の迅速なプロジェクト構築の実装

目次1. プロジェクトの構築2. Vue3 体験 + Vant 紹介2020年9月18日にvue.j...

nginx.pid を開く際の失敗と無効の解決策

目次1. 問題の説明2. 問題分析3. 解決策解決策1: ディレクトリを作成する解決策2: 構成ファ...

Flutterを使用して移動可能なスタックウィジェット機能を作成する

この投稿では、キャンバスとドラッグ可能なノード インターフェースを使用するデスクトップおよび Web...

HTML tbody の使用

構造化テーブル (IExplore のみ) 1) 行ごとにグループ化<thead> .....

MySQL のソート関数 field() の詳細な例

序文私たちの日常の開発プロセスでは、ソートが頻繁に使用され、そのような要求がある場合もあります。たと...