So implementieren Sie eine Multi-Terminal-Bridging-Plattform basierend auf WebSocket in JS

So implementieren Sie eine Multi-Terminal-Bridging-Plattform basierend auf WebSocket in JS

1. Was zu debuggen ist

Wir müssen vor allem wissen, was zu debuggen ist und was das Endergebnis sein wird:

1. Debuggen Sie die Schnittstelle, geben Sie die Schnittstellenadresse ein und erhalten Sie das entsprechende Ergebnis. Sie können mehrere Geräte gleichzeitig debuggen.

2. Debuggen Sie jsapi und geben Sie die entsprechende Methode ein, dann kann der Effekt im Newsclient angezeigt werden.

In Bezug auf die Debugging-Schnittstelle haben wir tatsächlich eine Möglichkeit zum einfachen Debuggen, es gibt jedoch zwei Einschränkungen: Android-System und Beta-Client, also Überbrückung über den Chrome-Browser. Allerdings ist diese Methode im iOS-System und in der offiziellen Version des Clients wirkungslos.

2. Funktionen von WebSocket

Das größte Merkmal des WebSocket-Protokolls besteht darin, dass der Server aktiv Informationen an den Client senden kann und der Client ebenfalls aktiv Informationen an den Server senden kann. Es handelt sich um einen echten gleichberechtigten Dialog in beide Richtungen und eine Art Server-Push-Technologie.

Weitere Features sind:

1. Da es auf dem TCP-Protokoll basiert, ist die serverseitige Implementierung relativ einfach.

2. Gute Kompatibilität mit dem HTTP-Protokoll. Die Standardports sind ebenfalls 80 und 443, und während der Handshake-Phase wird das HTTP-Protokoll verwendet, sodass es während des Handshakes nicht leicht blockiert wird und durch verschiedene HTTP-Proxyserver geleitet werden kann.

3. Das Datenformat ist relativ leichtgewichtig, mit geringem Leistungsaufwand und effizienter Kommunikation.

4. Es können sowohl Text- als auch Binärdaten gesendet werden.

5. Es gibt keine Same-Origin-Einschränkung, der Client kann mit jedem Server kommunizieren.

6. Die Protokollkennung ist ws (oder wss, wenn verschlüsselt) und die Server-URL ist die URL.

3. Socket-Verbindung herstellen

Um die Debugging-Ziele zu erreichen, die wir in Teil 1 festgelegt haben, müssen wir hier die folgenden Funktionen implementieren:

1. Der PC entspricht dem Raumbesitzer. Nachdem der Raum erstellt wurde, können andere Geräte den Raum betreten. Ein Gerät kann nur einen Raum betreten.

2. Der Client verfügt über einen Trennungs- und Wiederverbindungsmechanismus. Wenn die Verbindung zum Client getrennt wird, können Sie versuchen, die Verbindung wiederherzustellen.

3. Der Server verfügt über einen Heartbeat-Erkennungsmechanismus. Wenn ein neues Gerät eingeht oder ein vorheriges Gerät ausgeht, sollte die Geräteliste im aktuellen Raum rechtzeitig aktualisiert werden.

3.1 So erstellen Sie einen Raum

Geben Sie die Raum-ID im Browser ein. Wenn der Browser erfolgreich eine WebSocket-Verbindung mit dem Server herstellt, wird im Browser ein entsprechender QR-Code erstellt. Scannen Sie mit WeChat/Mobile QQ oder anderen Geräten, die QR-Codes scannen, und Sie können über das voreingestellte Schemaprotokoll zur entsprechenden Debugging-Seite im News-Client springen.

Wenn der Client erfolgreich eine WebSocket-Verbindung mit dem Server herstellt, ist dies gleichbedeutend mit einem erfolgreichen Betreten des Raums und auf dem PC wird ein entsprechendes Symbol angezeigt.

ws.open(Server-ID)
    .then(() => {
        // Nachdem der PC erfolgreich eine Verbindung hergestellt hat setStatus("linked"); // Aktualisiere den Status der Seite // Generiere einen QR-Code qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
            setCodeUrl(url);
        });
    })
    .catch(e => {
        // Verbindung konnte nicht hergestellt werden console.error(e);
        Modal.error({ title: "Es gibt ein Problem mit dem aktuellen Server und es wird repariert" });
        setStatus("Verknüpfung aufheben");
    });

3.2 Wiederholungsmechanismus für die Trennung des Clients

Es gibt eine Funktion der Seite auf dem mobilen Endgerät. Wenn der Bildschirm schwarz wird oder aus anderen Gründen, trennt der Client automatisch die Socket-Verbindung.

Um das Debuggen zu erleichtern, müssen Sie nach dem Trennen der Verbindung nicht manuell klicken oder die Seite jedes Mal erneut aufrufen. Ich habe hier einen einfachen Mechanismus zum Trennen und Wiederherstellen der Verbindung implementiert. Wenn die WebSocket-Verbindung getrennt wird, wird der Onclose-Rückruf ausgeführt. Daher können wir im Onclose-Ereignis einen Wiederverbindungsmechanismus implementieren.

Gleichzeitig habe ich, um unbegrenzte Wiederverbindungsversuche zu verhindern, auch hier ein Limit gesetzt. Die maximale Anzahl der Wiederverbindungen beträgt 3. Wenn die Verbindung nach 3 Mal nicht wiederhergestellt wird, wird die Verbindung beendet; wenn die Wiederverbindung erfolgreich ist, wird die Anzahl der Wiederverbindungen auf 3 zurückgesetzt.

Beim Trennen der Verbindung:

// Beim Trennen der Verbindung ws.onclose(() => {
    Timer = setzeTimeout(() => {
        setStatus("Verknüpfung aufheben");
        setCodeUrl("");
    }, 500);

    erneute Verbindungsnummer--;
    // Beschränke die Anzahl der Wiederverbindungen if (reconnectNum >= 0) {
        _open(); //Versuchen Sie, die Verbindung wiederherzustellen}
});

Wenn die Verbindung erfolgreich ist:

ws.open(serverId).then(() => {
    // Nachdem der PC erfolgreich eine Verbindung hergestellt hat + reconnectNum = 3;
    +Timer und ClearTimeout(Timer);

    setStatus("linked"); // Den Status der Seite aktualisieren // Einen QR-Code generieren qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
        setCodeUrl(url);
    });
});

3.3 Herzschlagerkennung

Genau wie beim Chatten in einer QQ-Gruppe ist auf einen Blick klar, wer online ist. Wenn jemand die Chat-Gruppe betritt oder verlässt, muss der Host benachrichtigt werden und die Gruppenliste muss zeitnah aktualisiert werden.

Es gibt zwei Haupttypen der Heartbeat-Erkennung: vom Client initiierte Heartbeat-Erkennung und vom Server verwaltete Heartbeat-Erkennung. Schauen wir uns diese beiden Typen an:

1. Vom Client initiierter Heartbeat: In festgelegten Abständen werden Ping-Daten an den Server gesendet. Unter normalen Umständen sendet der Server einen Pong an den Client zurück. Wenn der Client dies über das Onmessage-Ereignis überwachen kann, bedeutet dies, dass die Anforderung normal ist.

2. Vom Server verwalteter Heartbeat: Überprüfen Sie in regelmäßigen Abständen den Status aller Verbindungen. Wenn der Status getrennt ist, entfernen Sie ihn aus der Liste.

Hier verwende ich die vom Server verwaltete Heartbeat-Erkennung. Wenn sich die Anzahl der Geräte im Raum ändert, überträgt der Server die neueste Geräteliste an den Client:

//Verbindungsstatus des Clients kontinuierlich überwachen //Wenn die Verbindung getrennt wird, den Client löschen let aliveClients = new Map();
let lastAliveLength = neue Map();
setzeIntervall(() => {
    lass Kunden = {};
    wss.clients.forEach(Funktion each(ws) {
        wenn (ws.isAlive === false) {
            returniere ws.terminate();
        }
        const serverId = ws.serverId;
        wenn (Clients[Server-ID]) {
            Clients[Server-ID].push(ws);
        } anders {
            Clients[Server-ID] = [ws];
        }

        ws.isAlive = falsch;
        ws.ping(() => {});
    });
    für (Server-ID in Clients lassen) {
        aliveClients.set(serverId, clients[serverId]);
        const Länge = Clients[Server-ID].Länge;

        // Wenn sich die Anzahl der mit der aktuellen Server-ID verbundenen Geräte ändert, senden Sie eine Nachricht, wenn (Länge !== lastAliveLength.get(serverId)) {
            // Senden Sie Nachrichten an alle Geräte mit der aktuellen Server-ID sendAll("devices", clients[serverId], serverId);

            //Speichern Sie die Anzahl der letzten Verbindungen der aktuellen Server-ID. lastAliveLength.set(serverId, length);
        }
    }

    const Größe = wss.clients.size;

    console.log("Verbindungsnummer: ", Größe, neues Date().toTimeString());
}, 2000);

4. Debuggen Sie die Schnittstelle

Wir haben in Abschnitt 3 den PC und den News-Client erfolgreich verbunden. Wie kommunizieren wir also Daten zwischen den beiden Enden?

4.1 Schnittstellen-Debugging

Wir übergeben hier 3 Felder:

1.serverId: die Zimmernummer. Der Server muss die Informationen an alle Mitglieder mit der Server-ID senden.

2.Typ: Geben Sie ein, was diese Anweisung tun soll;

3.msg: die eingehenden Parameter;

Beim Debuggen der Schnittstelle werden folgende Parameter übergeben:

Konstante Parameter = {
    Typ: "post", // Typ Nachricht: {
        // Parameter-URL: „https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336“
    }
};

Wenn der Client die Schnittstellenanforderung normal abschließt, werden das Schnittstellenergebnis, das Cookie und die Geräteinformationen an den PC zurückgegeben:

// Anforderungsmethode const post = url => {
    wenn (Fenster.TencentNews && Fenster.TencentNews.post) {
        Fenster.TencentNews.post(URL, {}, Fenster[ID], {LoginType: "qqorweixin"}, {});
    } sonst wenn (window.TencentNews && window.TencentNews.postData) {
        Fenster.TencentNews.postData(URL, '{"a":"b"}', ID, "requestErrorCallback");
    }
};

// Vom mobilen Endgerät an den Server gesendete Daten ws.send({
    Typ: "postCb", // Ausführungsergebnisnachricht: {
        Methode: "post",
        Ergebnis,
        Cookie: Dokument.Cookie,
        appInfo
    }
});

Auf diese Weise können die Ergebnisse auf dem Frontend angezeigt werden und es handelt sich um eine echte Datenanforderung.

4.2 Speicherung historischer Aufzeichnungen

Was historische Aufzeichnungen angeht, haben unsere Mitschüler während des Gerichtsverfahrens immer noch dringenden Bedarf daran. Andernfalls müssen Sie die vorherige Schnittstellenadresse jedes Mal erneut eingeben oder einfügen, wenn Sie sie testen möchten, was sehr umständlich ist.

Wir speichern relativ vollständige Informationen wie die vom Benutzer angeforderte URL, die zurückgegebenen Ergebnisse, Cookies, Geräteinformationen usw. im Boss und speichern nur historische URLs lokal. Wenn der Benutzer die vorherige Schnittstelle erneut testen muss, klicken Sie einfach darauf. Wenn Sie die zuvor debuggte Schnittstelle anzeigen müssen, können Sie sie auf Hawkeye überprüfen.

Die lokale Speicherung erfolgt über „localStorage“. Noch wichtiger ist, dass wir auch die reaktionsfähigen Tools von mobx verwenden, sodass wir die Ergebnisse unmittelbar nachdem der Benutzer die Anfrage abgeschlossen hat, im Verlaufsprotokoll an der Seite sehen können.

5. Debuggen von JSAPI im News-Client

Zusätzlich zum Debuggen von Schnittstellen können Sie in einigen neuen Clients auch JSAPI debuggen. Es gibt zwei Möglichkeiten, die JSAPI unseres News-Clients aufzurufen:

// Rufen Sie direkt window.TencentNews.login("qqorweixin", isLogined => console.log(isLogined)) auf.

// Aufrufen von window.TencentNews.invoke("login", "qqorweixin", isLogined => console.log(isLogined));

Hier habe ich mich entschieden, die Invoke-Methode zum Aufrufen von jsapi zu verwenden.

Der PC initiiert einen JSAPI-Aufruf:

ws.send({
    Typ: "Anruf",
    Nachricht: {
        Methode: Methode,
        Parameter: slice.call(Argumente)
    }
});

Nachdem das mobile Terminal die Anforderung vom Server erhalten hat, ruft es die jsapi auf und gibt das Ausführungsergebnis an das PC-Terminal zurück:

const handleNewsApi = async (msg: beliebig): Promise<beliebig> => {
    warte auf tencentReady();

    const { Methode, Parameter } = Nachricht;
    gib ein neues Versprechen zurück (Auflösen => {
        window.TencentNews.invoke(Methode, ...Params, (Ergebnis: beliebig) => {
            lösen({ Methode, Ergebnis });
        });
    });
};

6. Fazit

Zu diesem Zeitpunkt ist meine „Multiterminal-Bridging-Plattform basierend auf WebSocket“ im Wesentlichen fertig. Es bleiben jedoch noch zwei Punkte, die kurz erläutert werden müssen.

6.1 Warum muss ich die Server-ID manuell eingeben?

Zuerst dachte ich, dass das System zufällig eine UUID generiert, wenn der Benutzer einen Raum erstellt. Dann dachte ich jedoch, dass sich die UUID ändert, wenn der Benutzer die Seite aktualisiert, sodass keine Verbindung zur vorherigen UUID mehr hergestellt werden kann. Daher bin ich hier auf manuelle Eingabe umgestiegen.

6.2 So stellen Sie sicher, dass alle Socket-Anfragen eines Clients in denselben Prozess gelangen

Wenn wir mehrere Prozesse im Hintergrund verwenden und nicht in die Benutzeranforderungen eingreifen, führt dies zu einem wahlfreien Zugriff auf die Anforderungen und generiert 400 Anforderungen. Schließlich besteht die anfängliche Verbindung in Prozess A, und jetzt wird die Anforderung an Prozess B gesendet, und Prozess B weiß nicht, wie er damit umgehen soll.

Es gibt mehrere Möglichkeiten, damit umzugehen:

Verfahren einführen Vorteil Mangel
Konsistenter Hashing-Algorithmus Alle Hosts und Verbindungen werden einem virtuellen Kreis von 0 bis 2^32-1 zugeordnet 1. Anwendbar auf Anwendungen im großen Maßstab;
2. Wenn ein Host oder Prozess abstürzt, sind die Auswirkungen gering
Komplexer in der Umsetzung
Nginx-Verteilung Der integrierte IP-Hash kann einen Lastenausgleich erreichen.
Die gleiche IP wird einem festen Backend-Server zugewiesen
Einfache Konfiguration Kann in einem Prozess konzentriert sein

Die Plattform, die ich hier verwende, ist eine interne Debugging-Plattform. Die Anzahl der Benutzer ist nicht groß, daher muss man nicht mit Kanonen auf Spatzen schießen. Darüber hinaus haben wir nur eine Maschine, sodass wir davon ausgehen, dass dieselbe IP in denselben Prozess eintritt. Hier übernehme ich die ip_hash-Idee von nginx: Wenn die Anforderung beim Hauptprozess eingeht, führe ich eine gewichtete Berechnung der IP durch und nehme dann den Modul entsprechend der Anzahl der Prozesse.

Natürlich kann bei dieser Methode auch das Problem zu vieler Socket-Verbindungen in einem Prozess auftreten, aber es ist völlig akzeptabel, wenn die Anzahl der Benutzer nicht groß ist (ich habe auch andere Methoden für dieses Problem in Betracht gezogen, z. B. die Wasserfallflussmethode. Jedes Mal, wenn einem untergeordneten Prozess eine Verbindung zugewiesen wird, wird zuerst der Prozess mit der geringsten Anzahl von Verbindungen ermittelt und dann wird die Verbindung diesem Prozess zugewiesen. Allerdings muss jedes Mal eine Tabelle gepflegt und berechnet werden).

6.3 Kommunikation zwischen mehreren Prozessen

Wenn sich im selben Raum die Socket-Verbindung auf der PC-Seite und die Verbindungen auf mehreren mobilen Endgeräten nicht im selben Prozess befinden, kommt es zu einem prozessübergreifenden Problem. Als extremes Beispiel: Jede Socket-Verbindung befindet sich in einem anderen Prozess. Daher müssen wir überlegen, wie wir andere Prozesse benachrichtigen, dass eine Anforderung an den Client gesendet werden muss.

Eine relativ einfache Möglichkeit, unseren Mechanismus zu nutzen, besteht darin, dass jeder PC-Benutzer der Host ist und einen Raum erstellen kann. Mobilgeräte sind Mitglieder des Raums. Jeder Raum ist unabhängig und stört sich nicht gegenseitig. Auf diese Weise fügen wir alle Socket-Verbindungen im Raum über die Raumkennung in denselben Prozess ein, sodass keine prozessübergreifenden Probleme auftreten. Ein Problem bei diesem Ansatz besteht jedoch darin, dass bei zu vielen Verbindungen in einem Raum derselbe Prozess erforderlich ist, um sie alle zu verarbeiten, während andere Prozesse inaktiv sind.

Sie können auch Redis verwenden: Verwenden Sie den Publish/Subscribe-Modus von Redis, um die Raum-ID und Informationen im aktuellen Prozess an andere Prozesse zu senden. Andere Prozesse haben Socket-Verbindungen mit derselben Raum-ID und führen entsprechende Vorgänge aus.

Oben sind die Details, wie JS eine Multi-Terminal-Bridging-Plattform auf Basis von WebSocket implementiert. Weitere Informationen zur Multi-Terminal-Bridging-Plattform von JS auf Basis von WebSocket finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Verwendung von jsencrypt.js für die RSA-Verschlüsselungsübertragung von Js-Parametern
  • Methode zur Implementierung einer Blob-verschlüsselten Videoquelladresse mithilfe von Javascript
  • JS implementiert den Effekt der Echtzeit-Nachrichtenaufforderung für WebSocket-Long-Polling
  • Detaillierte Erläuterung der JavaScript WebSocket-Technologie
  • Websocket umgeht direkt das JS-Verschlüsselungsbeispiel und das Ideenprinzip

<<:  MySQL 5.6.23 Installations- und Konfigurations-Umgebungsvariablen-Tutorial

>>:  Grafisches Tutorial zur Installation und Konfiguration von MySQL 8.0.11 und MacOS 10.13

Artikel empfehlen

Neue Funktionen in MySQL 8.0: Hash Join

Das MySQL-Entwicklungsteam hat am 14. Oktober 201...

Vor- und Nachteile gängiger MySQL-Speicher-Engines

Inhaltsverzeichnis Alle Speicher-Engines anzeigen...

So setzen Sie das Root-Passwort in Linux mysql-5.6 zurück

1. Überprüfen Sie, ob der MySQL-Dienst gestartet ...

Eine kurze Einführung in MySQL InnoDB ReplicaSet

Inhaltsverzeichnis 01 Einführung in InnoDB Replic...

SSH-Schlüsselpaare von einer oder mehreren Linux-Instanzen trennen

Schlüsselpaar trennen Trennen Sie SSH-Schlüsselpa...

Tutorial und Praxis zu den virtuellen Speichereinstellungen unter Linux

Was ist virtueller Speicher? Zunächst werde ich e...

Eine kurze Diskussion über den magischen Schrägstrich im Nginx Reverse Proxy

Beim Konfigurieren des Nginx-Reverse-Proxys könne...

Grafisches Tutorial zur Installation und Konfiguration von MySQL 8.0.22 winx64

Das Tutorial zur Datenbankinstallation von MySQL-...

So erstellen Sie einen vollständigen Samba-Server unter Linux (CentOS-Version)

Vorwort smb ist der Name eines Protokolls, das fü...

So ermitteln Sie, ob das Linux-System auf VMware installiert ist

Wie kann festgestellt werden, ob das aktuelle Lin...

Detaillierter Prozess der NTP-Serverkonfiguration unter Linux

Inhaltsverzeichnis 1. Umgebungskonfiguration 1.NT...

Vue.js Leistungsoptimierung N Tipps (es lohnt sich, sie zu sammeln)

Inhaltsverzeichnis Funktionskomponenten Aufteilun...

Zusammenfassung zum Erlernen von Docker-Befehlen in einem Artikel

Inhaltsverzeichnis Einführung Spiegel-Repository ...