CocosCreator Universal Framework Design Network

CocosCreator Universal Framework Design Network

Vorwort

Es ist relativ einfach, in Cocos Creator eine HTTP-Anfrage zu initiieren, aber viele Spiele möchten eine lange Verbindung zum Server aufrechterhalten, damit der Server aktiv Nachrichten an den Client senden kann, anstatt immer Anfragen vom Client zu initiieren. Dies gilt insbesondere für Spiele mit hohen Echtzeitanforderungen. Hier entwerfen wir ein allgemeines Netzwerk-Framework, das leicht auf unsere Projekte angewendet werden kann.

WebSocket verwenden

Bevor wir dieses Netzwerk-Framework implementieren, wollen wir uns zunächst mit WebSocket vertraut machen. Websocket ist ein auf TCP basierendes Vollduplex-Netzwerkprotokoll, das es Webseiten ermöglicht, dauerhafte Verbindungen herzustellen und eine bidirektionale Kommunikation durchzuführen. Die Verwendung von WebSocket in Cocos Creator kann in H5-Webspielen verwendet werden und unterstützt auch die nativen Plattformen Android und iOS.

Erstellen eines WebSocket-Objekts

Bei der Verwendung von WebSocket sollte der erste Schritt darin bestehen, ein WebSocket-Objekt zu erstellen. Der Konstruktor des WebSocket-Objekts kann zwei Parameter übergeben, der erste ist die URL-Zeichenfolge und der zweite ist die Protokollzeichenfolge oder das Zeichenfolgenarray, das die zulässigen Unterprotokolle angibt. Der Server muss eines davon auswählen, um es zurückzugeben, bevor eine Verbindung hergestellt wird, aber wir verwenden es im Allgemeinen nicht.

Der URL-Parameter ist sehr wichtig und besteht hauptsächlich aus vier Teilen: Protokoll, Adresse, Port und Ressource.

Beispiel: ws://echo.websocket.org:

  • Protokoll: Erforderlich. Das Standardprotokoll ist ws. Wenn eine sichere Verschlüsselung erforderlich ist, verwenden Sie wss.
  • Adresse: Erforderlich, kann eine IP oder ein Domänenname sein, natürlich wird die Verwendung des Domänennamens empfohlen.
  • Port: Optional. Wenn nicht angegeben, ist der Standardport für ws 80 und der Standardport für wss 443.
  • Ressource: Optional, normalerweise ein Ressourcenpfad nach dem Domänennamen, wir brauchen ihn grundsätzlich nicht.

Websocket-Status

Websocket hat 4 Zustände, die über die Eigenschaft readyState abgefragt werden können:

  • 0 CONNECTING Die Verbindung konnte nicht hergestellt werden.
  • 1 OPEN Die WebSocket-Verbindung ist hergestellt und die Kommunikation ist möglich.
  • 2 SCHLIESSEN: Die Verbindung wird einem schließenden Handshake unterzogen oder die Methode close() wurde aufgerufen.
  • 3 GESCHLOSSEN Die Verbindung wurde geschlossen.

WebSocket-API

Websocket hat nur zwei APIs, void send(data) zum Senden von Daten und void close(code, reason) zum Schließen der Verbindung.

Die Sendemethode empfängt nur einen Parameter – die zu sendenden Daten, die einen der folgenden vier Typen haben können: string | ArrayBufferLike | Blob | ArrayBufferView.

Wenn die zu sendenden Daten binär sind, können wir den Binärtyp über die BinaryType-Eigenschaft des WebSocket-Objekts angeben. BinaryType kann nur auf „Blob“ oder „Arraybuffer“ gesetzt werden, der Standardwert ist „Blob“. Wenn wir relativ feste Daten wie Dateien zum Schreiben auf die Festplatte übertragen möchten, verwenden Sie Blob. Wenn Sie Objekte übertragen möchten, die im Speicher verarbeitet werden, verwenden Sie den flexibleren Arraybuffer. Wenn Sie einen Blob aus anderen Nicht-Blob-Objekten und Daten erstellen möchten, müssen Sie den Blob-Konstruktor verwenden.

Beim Senden von Daten hat der Beamte 2 Vorschläge:

  • Überprüfen Sie, ob der ReadyState des WebSocket-Objekts OFFEN ist, und senden Sie nur, wenn dies der Fall ist.
  • Überprüfen Sie, ob der bufferedAmount des WebSocket-Objekts 0 ist, und senden Sie es nur, wenn dies der Fall ist (um eine Ansammlung von Nachrichten zu vermeiden, gibt diese Eigenschaft die Länge der Daten an, die nach dem Aufruf von „Send“ im WebSocket-Puffer angesammelt, aber nicht tatsächlich gesendet wurden).

Die Methode „close“ akzeptiert zwei optionale Parameter. „Code“ stellt den Fehlercode dar. Wir sollten 1000 oder eine Ganzzahl zwischen 3000 und 4999 übergeben. „Reason“ kann verwendet werden, um den Grund für das Schließen anzugeben. Die Länge darf 123 Bytes nicht überschreiten.

WebSocket-Rückruf

Websocket stellt uns 4 Rückruffunktionen zum Binden zur Verfügung:

  • onopen: wird aufgerufen, nachdem die Verbindung erfolgreich hergestellt wurde.
  • onmessage: wird aufgerufen, wenn eine Nachricht eingeht: Das übergebene Objekt hat ein Datenattribut, das ein String, ein Blob oder ein Arraybuffer sein kann.
  • onerror: wird aufgerufen, wenn ein Netzwerkfehler auftritt: Das übergebene Objekt hat ein Datenattribut, das normalerweise eine Zeichenfolge ist, die den Fehler beschreibt.
  • onclose: wird aufgerufen, wenn die Verbindung geschlossen wird: Das übergebene Objekt hat Attribute wie Code, Grund, wasClean usw.

Hinweis: Wenn ein Netzwerkfehler auftritt, wird zuerst onerror und dann onclose aufgerufen. Unabhängig vom Grund für die Verbindungsschließung wird onclose aufgerufen.

Echo-Beispiel

Nachfolgend sehen Sie den Code der Echo-Demo auf der offiziellen WebSocket-Website. Sie können ihn in eine HTML-Datei schreiben und mit einem Browser öffnen. Nach dem Öffnen wird automatisch eine WebSocket-Verbindung hergestellt. Wenn die Verbindung hergestellt ist, wird aktiv die Nachricht „WebSocket rockt“ gesendet. Der Server gibt die Nachricht zurück, löst onMessage aus, druckt die Informationen auf dem Bildschirm aus und schließt dann die Verbindung. Weitere Einzelheiten finden Sie unter: http://www.websocket.org/echo.html17

Das Standard-URL-Präfix ist wss. Da wss nicht in der richtigen Reihenfolge ist, können Sie nur über ws eine Verbindung herstellen. Wenn ws auch nicht in der richtigen Reihenfolge ist, können Sie versuchen, eine Verbindung zu dieser Adresse herzustellen: ws://121.40.165.18:8800. Dies ist eine kostenlose WebSocket-Test-URL in China.

Entwurfsrahmen

Ein allgemeines Netzwerk-Framework muss in der Lage sein, die unterschiedlichen Anforderungen verschiedener Projekte zu unterstützen, vorausgesetzt, es ist universell. Erfahrungsgemäß lauten die gemeinsamen Anforderungen wie folgt:

  • Aufgrund von Unterschieden in den Benutzerprotokollen können Spiele JSON-, Protobuf-, Flatbuffer- oder benutzerdefinierte Binärprotokolle übertragen.
  • Aufgrund der Unterschiede in den zugrunde liegenden Protokollen verwenden wir möglicherweise Websocket oder wx.websocket für WeChat-Spiele oder hoffen, sogar auf der nativen Plattform Protokolle wie TCP/UDP/KCP verwenden zu können.
  • Anmeldeauthentifizierungsprozess: Wir sollten eine Anmeldeauthentifizierung durchführen, bevor wir eine lange Verbindung verwenden, und verschiedene Spiele haben unterschiedliche Anmeldeauthentifizierungsmethoden.
  • Behandlung von Netzwerkausnahmen, z. B. wie lange ist das Timeout, wie ist die Leistung nach dem Timeout, soll die Benutzeroberfläche beim Anfordern und Warten auf die Serverantwort blockiert werden, wie ist die Leistung nach der Trennung des Netzwerks, ob die Verbindung automatisch wiederhergestellt wird oder ob der Player zum Wiederherstellen auf die Schaltfläche „Wiederverbinden“ klickt und ob die Nachrichten während der Netzwerktrennungsphase nach der Wiederherstellung der Verbindung erneut gesendet werden? Und so weiter.
  • Verarbeitung mehrerer Verbindungen : Einige Spiele müssen möglicherweise mehrere verschiedene Verbindungen unterstützen, im Allgemeinen nicht mehr als zwei. Beispielsweise ist eine Hauptverbindung für die Verarbeitung von Geschäftsnachrichten wie der Lobby verantwortlich, und eine Kampfverbindung ist direkt mit dem Kampfserver oder mit dem Chat-Server verbunden.

Basierend auf den oben genannten Anforderungen teilen wir die Funktionsmodule auf, um eine hohe Kohäsion und geringe Kopplung der Module sicherzustellen.

ProtocolHelper-Protokollverarbeitungsmodul - Wenn wir einen Puffer erhalten, müssen wir möglicherweise das Protokoll oder die ID kennen, die diesem Puffer entspricht. Wenn wir beispielsweise während der Anforderung den Rückruf zur Antwortverarbeitung übergeben, besteht die gängige Vorgehensweise möglicherweise darin, eine automatisch inkrementierende ID zu verwenden, um jede Anforderung zu unterscheiden, oder die Protokollnummer zu verwenden, um verschiedene Anforderungen zu unterscheiden. Dies müssen Entwickler implementieren. Müssen wir auch die Länge des Pakets aus dem Puffer abrufen? Welche Paketlänge ist sinnvoll? Wie sieht ein Heartbeat-Paket aus usw.

Socket-Modul - implementiert die grundlegendste Kommunikationsfunktion. Definieren Sie zunächst die Socket-Schnittstellenklasse ISocket, definieren Sie Schnittstellen wie Verbinden, Schließen, Datenempfangen und -senden und erben und implementieren Sie diese Schnittstellen anschließend an Unterklassen.

NetworkTips-Netzwerkanzeigemodul – realisiert die Anzeige von Status wie Verbinden, Wiederverbinden, Laden, Netzwerktrennung usw. sowie die Abschirmung der Benutzeroberfläche.

NetNode-Netzwerkknoten – der sogenannte Netzwerkknoten, dessen Hauptaufgabe darin besteht, die oben genannten Funktionen in Reihe zu schalten und den Benutzern eine benutzerfreundliche Schnittstelle bereitzustellen.

NetManager verwaltet einen Singleton von Netzwerkknoten . Wir haben möglicherweise mehrere Netzwerkknoten (mehrere Verbindungen), daher wird hier ein Singleton zur Verwaltung verwendet. Es ist auch bequemer, einen Singleton zum Bedienen von Netzwerkknoten zu verwenden.

ProtokollHelfer

Eine einfache IProtocolHelper-Schnittstelle wird hier wie folgt definiert:

Exporttyp NetData = (Zeichenfolge | ArrayBufferLike | Blob | ArrayBufferView); // Protokoll-Hilfsschnittstelle Exportschnittstelle IProtocolHelper
{    
    getHeadlen(): Zahl; // Gibt die Länge des Paketheaders zurück getHearbeat(): NetData; // Gibt ein Heartbeat-Paket zurück getPackageLen(msg: NetData): Zahl; // Gibt die Länge des gesamten Pakets zurück checkPackage(msg: NetData): boolean; // Überprüft, ob die Paketdaten zulässig sind getPackageId(msg: NetData): Zahl; // Gibt die Paket-ID oder den Protokolltyp zurück }

Buchse

Hier wird eine einfache ISocket-Schnittstelle definiert, wie unten gezeigt:

//Socket-Schnittstelle exportiere Schnittstelle ISocket {    
    onConnected: (Ereignis) => void; //Verbindung callbackonMessage: (msg: NetData) => void; //Nachricht callbackonError: (Ereignis) => void; //Fehler callbackonClosed: (Ereignis) => void; //Schließen callbackconnect(ip: Zeichenfolge, Port: Nummer); //Verbindung interfacesend(Puffer: NetData); //Daten werden gesendet interfaceclose(Code?: Nummer, Grund?: Zeichenfolge); //Schnittstelle schließen}

Als nächstes implementieren wir einen WebSock, der von ISocket erbt. Wir müssen nur die Schnittstellen „Connect“, „Send“ und „Close“ implementieren. „Senden“ und „Schließen“ sind beides einfache Kapselungen von WebSockets, während „Verbinden“ eine URL entsprechend der übergebenen IP-Adresse, des Ports und anderer Parameter erstellen muss, um einen WebSocket zu erstellen und den Rückruf des WebSockets zu binden.

Exportklasse WebSock implementiert ISocket {    
    private _ws: WebSocket = null; // WebSocket-Objekt onConnected: (Ereignis) => void = null;    
    beiNachricht: (msg) => void = null;    
    bei Fehler: (Ereignis) => void = null;    
    onClosed: (Ereignis) => void = null;   
    verbinden(Optionen: beliebig) {        
    wenn (diese._ws) {            
        wenn (this._ws.readyState === WebSocket.CONNECTING) {                
            console.log("WebSocket-Verbindung wird hergestellt, warten Sie einen Moment …")                
            gibt false zurück;
        }
    }
    lass url = null;        
    wenn(optionen.url) {           
        url = Optionen.url;        
    } anders {            
        lass ip = options.ip;            
        let port = Optionen.port;            
        let Protokoll = Optionen.Protokoll;            
        URL = `${Protokoll}://${IP}:${Port}`;           
    }        
        this._ws = neuer WebSocket(URL);       
        this._ws.binaryType = options.binaryType ? options.binaryType : "Array-Puffer";        
        this._ws.onmessage = (Ereignis) => {           
        dies.onMessage(Ereignis.Daten);        
    };        
        dies._ws.onopen = dies.onConnected;        
        dies._ws.onerror = dies.onError;        
        dies._ws.onclose = dies.onClosed;       
        gibt true zurück;    
    }    
    sende(Puffer: NetData) {        
    wenn (this._ws.readyState == WebSocket.OPEN) {           
        this._ws.send(Puffer);           
        gibt true zurück;       
    }       
    gibt false zurück;    
    }    
    schließen(Code?: Nummer, Grund?: Zeichenfolge) {        
    dies._ws.close();    
    }
}

NetzwerkTipps

INetworkTips bietet eine sehr nützliche Schnittstelle sowie Wiederverbindungs- und Anforderungsschalter. Das Framework ruft sie zum richtigen Zeitpunkt auf. Wir können INetworkTips erben und unsere netzwerkbezogenen Eingabeaufforderungsinformationen anpassen. Es ist zu beachten, dass diese Schnittstellen **mehrmals** aufgerufen werden können.

// Netzwerktipps-Schnittstelle Exportschnittstelle INetworkTips {    
    connectTips(isShow: boolean): void;    
    Tipps erneut verbinden(isShow: boolean): void;    
    requestTips(isShow: boolean): void;
}

NetNode

NetNode ist der kritischste Teil des gesamten Netzwerk-Frameworks. Eine NetNode-Instanz stellt ein vollständiges Verbindungsobjekt dar. Basierend auf NetNode können wir es problemlos erweitern. Seine Hauptaufgaben sind:

Verbindungswartung

  • Verbindungsaufbau und Authentifizierung (ob und wie eine Authentifizierung erfolgt, wird durch den Rückruf des Benutzers bestimmt)
  • Datenübertragung nach Trennung und Wiederherstellung der Verbindung
  • Der Heartbeat-Mechanismus stellt sicher, dass die Verbindung gültig ist (das Heartbeat-Paketintervall ist konfiguriert und der Inhalt des Heartbeat-Pakets wird durch ProtocolHelper definiert).
  • Schließen der Verbindung

Datenübertragung

  • Unterstützt erneute Übertragung bei Trennung und Timeout
  • Unterstützt einmaliges Senden (vermeidet gleichzeitiges, mehrmaliges Senden)

Datenempfang

  • Unterstützt kontinuierliche Überwachung
  • Unterstützt den Request-Response-Modus

Schnittstellenanzeige

  • Anpassbare Netzwerkverzögerung, kurzfristige Wiederverbindung und andere Statusleistung
  • Zuerst definieren wir zwei Aufzählungen, NetTipsType und NetNodeState, und die NetConnectOptions-Struktur zur Verwendung durch NetNode.
  • Als nächstes folgen die Mitgliedsvariablen von NetNode. NetNode-Variablen können in die folgenden Kategorien unterteilt werden:
  • NetNode-eigene Zustandsvariablen , wie etwa ISocket-Objekt, aktueller Zustand, Verbindungsparameter usw.
  • Verschiedene Rückrufe , einschließlich Verbindung, Trennung, Protokollverarbeitung, Netzwerkaufforderungen usw.
  • Verschiedene Timer , wie z. B. Heartbeat- und Wiederverbindungs-Timer.
  • Sowohl die Anforderungsliste als auch die Abhörliste werden zum Verarbeiten empfangener Nachrichten verwendet.

Als nächstes stellen wir die netzwerkbezogenen Mitgliedsfunktionen vor. Schauen wir uns zunächst die Initialisierung an und:

  • Die Init-Methode wird zum Initialisieren von NetNode verwendet, hauptsächlich um Verarbeitungsobjekte wie Socket und Protokoll anzugeben.
  • Die Connect-Methode wird verwendet, um eine Verbindung mit dem Server herzustellen.
  • Die Methode initSocket wird verwendet, um den Socket-Rückruf an den NetNode zu binden.
  • Die Methode updateNetTips wird zum Aktualisieren der Netzwerktipps verwendet.

Die Methode onConnected wird aufgerufen, nachdem die Netzwerkverbindung erfolgreich hergestellt wurde, und der Authentifizierungsprozess beginnt automatisch (wenn _connectedCallback festgelegt ist). Nachdem die Authentifizierung abgeschlossen ist, muss die Methode onChecked aufgerufen werden, damit NetNode in einen Kommunikationszustand wechselt. Im Falle einer Nichtauthentifizierung sollten wir keine Geschäftsanforderungen senden, aber Anforderungen wie die Anmeldeüberprüfung sollten an den Server gesendet werden. Mit dem Parameter force kann das Senden solcher Anforderungen an den Server erzwungen werden.

Der Empfang einer beliebigen Nachricht löst onMessage aus. Zuerst wird das Datenpaket überprüft. Die Überprüfungsregeln können in Ihrem eigenen ProtocolHelper implementiert werden. Wenn es sich um ein gültiges Datenpaket handelt, aktualisieren wir die Heartbeat- und Timeout-Timer – re-timen und finden schließlich die Verarbeitungsfunktion der Nachricht in _requests und _listener. Hier durchsuchen wir rspCmd. rspCmd wird aus getPackageId von ProtocolHelper entnommen. Wir können den Befehl oder die Sequenznummer des Protokolls zurückgeben und entscheiden, wie Anfrage und Antwort übereinstimmen.

onError und onClosed werden aufgerufen, wenn das Netzwerk ausfällt und geschlossen wird. Unabhängig davon, ob ein Fehler vorliegt oder nicht, wird onClosed letztendlich aufgerufen. Hier führen wir den Trennungs-Callback aus und führen die automatische Wiederverbindungsverarbeitung durch. Natürlich können Sie auch „close“ aufrufen, um den Socket zu schließen. Der Unterschied zwischen close und closeSocket besteht darin, dass closeSocket lediglich den Socket schließt – ich möchte den aktuellen NetNode weiterhin verwenden und möglicherweise beim nächsten Connect das Netzwerk wiederherstellen. Und mit „Schließen“ werden alle Zustände gelöscht.

Es gibt drei Möglichkeiten , eine Netzwerkanfrage zu initiieren :

Die Sendemethode sendet einfach Daten. Wenn das Netzwerk derzeit getrennt ist oder eine Überprüfung ausgeführt wird, werden die Daten in die Warteschlange _request eingetragen.

Die Anforderungsmethode übergibt den Rückruf beim Stellen einer Anforderung in Form eines Abschlusses. Der Rückruf wird ausgeführt, wenn die Antwort auf die Anforderung zurückkommt. Wenn mehrere identische Anforderungen gleichzeitig vorliegen, werden die Antworten dieser N Anforderungen nacheinander an den Client zurückgegeben, und die Antwortrückrufe werden ebenfalls nacheinander ausgeführt (es wird jeweils nur ein Rückruf ausgeführt).

Wenn wir nicht mehrere identische Anfragen haben möchten, können wir mit der Methode requestUnique sicherstellen, dass von jedem Typ gleichzeitig nur eine Anfrage vorhanden ist.

Der Grund, warum wir Traversal-_Requests verwenden, um sicherzustellen, dass es hier keine Duplikate gibt, besteht darin, dass wir in _Requests keine große Anzahl von Anfragen ansammeln und Timeouts oder abnormale Neuübertragungen keinen Rückstau von _Requests verursachen, da die Neuübertragungslogik von NetNode gesteuert wird und wir Benutzer daran hindern sollten, Anfragen zu initiieren, wenn die Netzwerkverbindung getrennt wird. Zu diesem Zeitpunkt wird im Allgemeinen eine Vollbildmaske angezeigt – eine Eingabeaufforderung wie Netzwerkschwankungen.

Wir haben zwei Arten von Rückrufen. Einer ist der oben erwähnte Anforderungsrückruf. Dieser Rückruf ist temporär und wird normalerweise sofort mit der Anforderungs-Antwort-Ausführung bereinigt. Der _listener-Rückruf ist permanent und muss manuell verwaltet werden, z. B. das Abhören beim Öffnen einer bestimmten Schnittstelle, das Schließen beim Verlassen oder das Abhören zu Beginn des Spiels. Geeignet für die Verarbeitung aktiver Push-Nachrichten vom Server.

Schließlich gibt es Timer im Zusammenhang mit Heartbeat und Timeout. Wir senden jedes _heartTime ein Heartbeat-Paket, und wenn wir nicht jedes _receiveTime ein vom Server zurückgegebenes Paket erhalten, stellen wir fest, dass das Netzwerk getrennt ist.

Um den vollständigen Code anzuzeigen, können Sie den Quellcode eingeben!

NetManager

NetManager wird zur Verwaltung von NetNode verwendet. Dies liegt daran, dass wir möglicherweise mehrere verschiedene Verbindungsobjekte unterstützen müssen, sodass wir einen NetManager zur Verwaltung von NetNode benötigen. Gleichzeitig kann uns NetManager als Singleton auch das Aufrufen des Netzwerks erleichtern.

Exportklasse NetManager {
	private statische _Instanz: NetManager = null;
	geschützte _Kanäle: {
		[Schlüssel: Nummer]: NetNode
	} = {};
	öffentliche statische getInstance(): NetManager {
		wenn (diese._instanz == null) {
			this._instance = neuer NetManager();
		}
		gib diese._Instanz zurück;
	} // Knoten hinzufügen und ChannelID zurückgeben    
	öffentliche setNetNode(neuerNode: NetNode, channelId: Nummer = 0) {
		this._channels[channelId] = neuer Knoten;
	} // Knoten entfernen    
	öffentliche removeNetNode(channelId: Nummer) {
		lösche dies._channels[channelId];
	} // Knotenverbindung aufrufen public connect(options: NetConnectOptions, channelId: number = 0): boolean {
		wenn (this._channels[channelId]) {
			gib dies zurück._channels[channelId].connect(Optionen);
		}
		gibt false zurück;
	} // Rufen Sie den Knoten auf, um public send(buf: NetData, force: boolean = false, channelId: number = 0) zu senden: boolean {
		lass Knoten = this._channels[channelId];
		wenn (Knoten) {
			gibt node.send(buf, force) zurück;
		}
		gibt false zurück;
	} // Eine Anfrage einleiten und die angegebene Callback-Funktion aufrufen, wenn das Ergebnis zurückgegeben wird public request(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force: boolean =
		false, Kanal-ID: Nummer = 0) {
		lass Knoten = this._channels[channelId];
		wenn (Knoten) {
			node.request(buf, rspCmd, rspObject, Tipps anzeigen, erzwingen);
		}
	} // Wie bei der Anfrage, aber vor der Anfrage wird zunächst ermittelt, ob sich bereits rspCmd in der Warteschlange befindet. Wenn ein Duplikat vorhanden ist, wird es direkt zurückgegeben public requestUnique(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force:
		boolean = false, channelId: Nummer = 0): boolean {
		lass Knoten = this._channels[channelId];
		wenn (Knoten) {
			gibt node.requestUnique(buf, rspCmd, rspObject, showTips, force) zurück;
		}
		gibt false zurück;
	} // Rufen Sie den Knoten zum Schließen auf. public close(code ? : number, reason ? : string, channelId: number = 0) {
		wenn (this._channels[channelId]) {
			gib dies zurück._channels[channelId].closeSocket(Code, Grund);
		}
	}

Testbeispiele

Als Nächstes verwenden wir ein einfaches Beispiel, um die grundlegende Verwendung des Netzwerk-Frameworks zu demonstrieren. Zunächst müssen wir eine einfache Schnittstelle für die Anzeige, 3 Schaltflächen (Verbinden, Senden, Schließen), 2 Eingabefelder (URL eingeben, zu sendenden Inhalt eingeben) und ein Textfeld (vom Server empfangene Daten anzeigen) zusammenstellen, wie in der folgenden Abbildung dargestellt.

Dieses Beispiel stellt eine Verbindung zur offiziellen Websocket-Adresse echo.websocket.org her. Dieser Server gibt alle Nachrichten, die wir ihm senden, unverändert an uns zurück.

Als nächstes implementieren Sie eine einfache Komponente. Hier wird eine neue NetExample.ts-Datei erstellt. Die Aufgabe ist sehr einfach. Während der Initialisierung wird NetNode erstellt und der standardmäßige Empfangs-Callback gebunden. Im Empfangs-Callback wird der vom Server zurückgegebene Text in msgLabel angezeigt. Als nächstes folgt die Implementierung mehrerer Schnittstellen zum Verbinden, Senden und Schließen:

// Unkritischer Code weggelassen @ccclassexport
Standardklasse NetExample erweitert cc.Component {
	@Eigenschaft(cc.Label)
	Textbezeichnung: cc.Label = null;
	@Eigenschaft(cc.Label)
	URL-Label: cc.Label = null;
	@Eigenschaft(cc.RichText)
	msgLabel: cc.RichText = null;
	private lineCount: Zahl = 0;
	beim Laden() {
		let Node = neuer NetNode();
		Node.init(neues WebSock(), neues DefStringProtocol());
		Node.setResponeHandler(0, (cmd: Zahl, Daten: NetData) => {
			wenn (this.lineCount > 5) {
				Lassen Sie idx = this.msgLabel.string.search("\n");
				this.msgLabel.string = this.msgLabel.string.substr(idx + 1);
			}
			this.msgLabel.string += `${data}\n`;
			++diese.Zeilenanzahl;
		});
		NetManager.getInstance().setNetNode(Knoten);
	}
	beiVerbindenKlick() {
		NetManager.getInstance().verbinden({
			URL: diese.URL-Bezeichnung.Zeichenfolge
		});
	}
	beiSendenKlick() {
		NetManager.getInstance().send(this.textLabel.string);
	}
	beiKlickTrennenderVerbindung() {
		NetManager.getInstance().close();
	}
}

Nachdem der Code abgeschlossen ist, mounten Sie ihn unter dem Canvas-Knoten der Szene (andere Knoten sind auch OK) und ziehen Sie dann das Label und den RichText in der Szene in das Eigenschaftenfenster unseres NetExample:

Der Laufeffekt ist wie folgt:

Zusammenfassung

Wie Sie sehen, ist die Verwendung von Websocket sehr einfach. Während des Entwicklungsprozesses werden wir auf verschiedene Anforderungen und Probleme stoßen. Wir müssen ein gutes Design implementieren und die Probleme schnell lösen.

Einerseits müssen wir ein tiefes Verständnis der von uns verwendeten Technologie haben. Wie ist die zugrunde liegende Protokollübertragung von WebSocket implementiert? Was ist der Unterschied zwischen TCP und HTTP? Kann UDP für websocket-basierte Übertragungen verwendet werden? Muss ich beim Senden von Daten mithilfe von WebSocket den Datenstrom selbst in Unterpakete aufteilen (das WebSocket-Protokoll stellt die Integrität des Pakets sicher)? Kommt es beim Senden von Daten zu einer Ansammlung von Sendepuffer (überprüfen Sie bufferedAmount)?

Darüber hinaus müssen wir unsere Nutzungsszenarien und Bedürfnisse verstehen. Je gründlicher wir die Bedürfnisse verstehen, desto besser ist das Design. Welche Anforderungen sind projektspezifisch und welche allgemein? Sind gemeinsame Anforderungen obligatorisch oder optional? Sollten wir verschiedene Änderungen in Klassen oder Schnittstellen kapseln und sie mithilfe von Polymorphismus implementieren? Oder Konfiguration bereitstellen? Rückrufbindung? Veranstaltungsbenachrichtigung?

Wir müssen ein gutes Framework entwerfen, das zum nächsten Projekt passt, und die Iterationen in jedem Projekt optimieren, damit wir umfassende Erfahrungen sammeln und die Effizienz verbessern können.

Oben finden Sie den detaillierten Inhalt des Netzwerks des allgemeinen Framework-Designs von CocosCreator. Weitere Informationen zum Netzwerk des Framework-Designs von CocosCreator finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung des Cocoscreater-Prefabs
  • So verwenden Sie residente Knoten für die Ebenenverwaltung in CocosCreator
  • So verwenden Sie CocosCreator zur Tonverarbeitung bei der Spieleentwicklung
  • CocosCreator ScrollView-Optimierungsreihe: Frame-Laden
  • Detaillierte Erläuterung des CocosCreator-Projektstrukturmechanismus
  • So verwenden Sie den CocosCreator-Objektpool
  • So zeigen Sie in CocosCreator eine Textur an der Wischposition an
  • Organisieren Sie die allgemeinen Wissenspunkte von CocosCreator
  • Umfassende Erklärung zum CocosCreator Hot Update
  • CocosCreator klassisches Einstiegsprojekt flappybird
  • So verwenden Sie CocosCreator zum Erstellen eines Schießspiels
  • So verwenden Sie einen Gamecontroller in CocosCreator

<<:  Detaillierte Erläuterung des zeitaufwändigen SQL-Beispiels für MySQL-Datensätze

>>:  Schritte zur Verwendung von autoconf zum Generieren von Makefile und Kompilieren des Projekts

Artikel empfehlen

Erweiterte MySQL-Datenbankabfrage und Mehrtabellenabfrage

MySQL-Abfrage für mehrere Tabellen Hinzufügen ein...

Centos8.3, Docker-Bereitstellung, Springboot-Projekt, tatsächliche Fallanalyse

Einführung Derzeit ist k8s sehr beliebt und ich h...

MySQL-Batch löschen großer Datenmengen

MySQL-Batch löschen großer Datenmengen Angenommen...

Beispiel für die Anzeige von Bildjalousien mit reinem CSS

Lassen Sie mich Ihnen zunächst den fertigen Effek...

Vue hält den Benutzer angemeldet (verschiedene Token-Speichermethoden)

Inhaltsverzeichnis So setzen Sie Cookies Nachteil...

Gängige Reparaturmethoden für die Trennung der MySQL Master-Slave-Replikation

Inhaltsverzeichnis 01 Problembeschreibung 02 Lösu...

Bestimmen Sie die Richtung der Mauseingabe basierend auf CSS

In einer Front-End-Technologiegruppe sagte ein Gr...

Eine kurze Einführung in JavaScript-Arrays

Inhaltsverzeichnis Einführung in Arrays Array-Lit...

So verwenden Sie das JavaScript-Strategiemuster zum Validieren von Formularen

Inhaltsverzeichnis Überblick Formularvalidierung ...

JavaScript-Timer zur Realisierung einer zeitlich begrenzten Flash-Sale-Funktion

In diesem Artikel wird der spezifische JavaScript...

Implementierung der Miniprogramm-Aufzeichnungsfunktion

Vorwort Bei der Entwicklung eines Miniprogramms b...

So exportieren Sie MySQL-Abfrageergebnisse in CSV

Um MySQL-Abfrageergebnisse in CSV zu exportieren,...

5 Tipps zum Schreiben von CSS, um Ihren Stil standardisierter zu gestalten

1. CSS alphabetisch ordnen Nicht in alphabetischer...