序文Cocos Creator で http リクエストを開始するのは比較的簡単ですが、多くのゲームでは、常にクライアントがリクエストを開始するのではなく、サーバーがクライアントにメッセージをアクティブにプッシュできるように、サーバーとの長い接続を維持することを望んでいます。これは、リアルタイム要件が高いゲームに特に当てはまります。ここでは、プロジェクトに簡単に適用できる一般的なネットワーク フレームワークを設計します。 Websocketの使用このネットワーク フレームワークを実装する前に、まず Websocket について理解しましょう。 Websocket は、Web ページが永続的な接続を作成し、双方向通信を実行できるようにする TCP ベースの全二重ネットワーク プロトコルです。 Cocos Creator で websocket を使用すると、H5 Web ゲームで使用でき、ネイティブ プラットフォームの Android および iOS もサポートされます。 Websocketオブジェクトの構築Websocket を使用する場合、最初のステップは Websocket オブジェクトを作成することです。 Websocket オブジェクトのコンストラクターは 2 つのパラメーターを渡すことができます。1 つ目は URL 文字列、2 つ目は受け入れ可能なサブプロトコルを指定するプロトコル文字列または文字列配列です。サーバーは接続を確立する前にそれらのうちの 1 つを選択して返す必要がありますが、通常は使用しません。 url パラメータは非常に重要であり、主にプロトコル、アドレス、ポート、リソースの 4 つの部分に分かれています。 たとえば、ws://echo.websocket.org:
Websocket ステータスWebsocket には 4 つの状態があり、readyState プロパティを通じて照会できます。
WebSocket APIWebsocket には、データを送信するための void send( data ) と接続を閉じるための void close( code, reason ) の 2 つの API しかありません。 send メソッドは、送信するデータという 1 つのパラメータのみを受け取ります。送信するデータは、string | ArrayBufferLike | Blob | ArrayBufferView の 4 つのタイプのいずれかになります。
データを送信する場合、担当者は 2 つの提案をしています。
close メソッドは、2 つのオプション パラメータを受け入れます。Code はエラー コードを表します。1000 または 3000 から 4999 までの整数を渡す必要があります。Reason は、閉じる理由を示すために使用できます。長さは 123 バイトを超えることはできません。 WebsocketコールバックWebsocket は、バインドするための 4 つのコールバック関数を提供します。
エコーの例以下は、Websocket 公式サイトの echo デモのコードです。これを html ファイルに記述して、ブラウザで開くことができます。開くと、Websocket 接続が自動的に作成されます。接続が確立されると、「WebSocket rocks」というメッセージがアクティブに送信されます。サーバーはメッセージを返し、onMessage をトリガーして、情報を画面に出力してから、接続を閉じます。詳細については、http://www.websocket.org/echo.html17 を参照してください。
デザインフレームワーク一般的なネットワーク フレームワークは、普遍性を前提として、さまざまなプロジェクトのさまざまな要件をサポートできる必要があります。経験によると、共通の要件は次のとおりです。
上記の要件に基づいて、モジュール間の高い凝集性と低い結合性を確保するために機能モジュールを分割します。 ProtocolHelper プロトコル処理モジュール - バッファを取得するときに、このバッファに対応するプロトコルまたは ID を知る必要がある場合があります。たとえば、リクエスト中に応答処理コールバックを渡す場合、自動増分 ID を使用して各リクエストを区別するか、プロトコル番号を使用して異なるリクエストを区別するのが一般的です。これらは、開発者が実装する必要があるものです。バッファからパケットの長さも取得する必要がありますか?パッケージの長さの妥当な範囲はどれくらいですか?ハートビート パケットはどのようなものか、など。 ソケット モジュール- 最も基本的な通信機能を実装します。まず、ソケット インターフェイス クラス ISocket を定義し、接続、クローズ、データの受信と送信などのインターフェイスを定義し、サブクラスがこれらのインターフェイスを継承して実装します。 NetworkTips ネットワーク表示モジュール- 接続中、再接続中、読み込み中、ネットワーク切断などのステータスの表示と UI のシールドを実現します。 NetNode ネットワーク ノード- いわゆるネットワーク ノードですが、実際には、その主な役割は上記の機能を直列に接続し、ユーザーに使いやすいインターフェイスを提供することです。 NetManager はネットワーク ノードのシングルトンを管理します。ネットワーク ノードは複数 (複数接続) になる可能性があるため、ここでは管理にシングルトンが使用されます。ネットワーク ノードの操作にもシングルトンを使用する方が便利です。 プロトコルヘルパーここでは、単純な IProtocolHelper インターフェイスが次のように定義されています。 export type NetData = (string | ArrayBufferLike | Blob | ArrayBufferView); // プロトコルヘルパーインターフェース export interface IProtocolHelper { getHeadlen(): number; // パケット ヘッダーの長さを返します getHearbeat(): NetData; // ハートビート パケットを返します getPackageLen(msg: NetData): number; // パケット全体の長さを返します checkPackage(msg: NetData): boolean; // パケット データが正当かどうかを確認します getPackageId(msg: NetData): number; // パケット ID またはプロトコル タイプを返します } ソケットここでは、次に示すように、単純な ISocket インターフェイスが定義されています。 //ソケットインターフェースエクスポートインターフェースISocket{ onConnected: (event) => void; //接続 callbackonMessage: (msg: NetData) => void; //メッセージ callbackonError: (event) => void; //エラー callbackonClosed: (event) => void; //閉じる callbackconnect(ip: string, port: number); //接続 interfaceend(buffer: NetData); //データ送信 interfaceclose(code?: number, reason?: string); //インターフェイスを閉じる} 次に、ISocket から継承する WebSock を実装します。実装する必要があるのは、connect、send、close の各インターフェースだけです。 Send と Close はどちらも Websocket の単純なカプセル化ですが、connect では渡された IP、ポート、その他のパラメータに従って URL を構築し、Websocket を作成して Websocket のコールバックをバインドする必要があります。 エクスポートクラスWebSockはISocketを実装します{ private _ws: WebSocket = null; // websocket オブジェクト onConnected: (event) => void = null; onMessage: (メッセージ) => void = null; onError: (イベント) => void = null; onClosed: (イベント) => void = null; 接続(オプション: 任意) { if (this._ws) { this._ws.readyState が WebSocket.CONNECTING の場合 { console.log("Websocket に接続しています。しばらくお待ちください...") false を返します。 } } url = null とします。 if(options.url) { url = オプション.url; } それ以外 { ip = options.ip とします。 port = options.port とします。 プロトコル = options.protocol とします。 url = `${プロトコル}://${ip}:${ポート}`; } this._ws = 新しい WebSocket(url); this._ws.binaryType = options.binaryType ? options.binaryType : "arraybuffer"; this._ws.onmessage = (イベント) => { this.onMessage(イベントデータ); }; this._ws.onopen = this.onConnected; this._ws.onerror = this.onError; this._ws.onclose = this.onClosed; true を返します。 } 送信(バッファ: NetData) { this._ws.readyState が WebSocket.OPEN の場合 { this._ws.send(バッファ); true を返します。 } false を返します。 } 閉じる(コード?: 数値、理由?: 文字列) { this._ws.close(); } } ネットワークのヒントINetworkTips は、非常に便利なインターフェイス、再接続、および要求スイッチを提供します。フレームワークは適切なタイミングでそれらを呼び出します。INetworkTips を継承して、ネットワーク関連のプロンプト情報をカスタマイズできます。これらのインターフェイスは **複数回** 呼び出される可能性があることに注意してください。 // ネットワークヒントインターフェースエクスポートインターフェースINetworkTips { connectTips(isShow: boolean): void; 再接続ヒント(isShow: boolean): void; リクエストヒント(isShow: boolean): void; } ネットノードNetNode は、ネットワーク フレームワーク全体の中で最も重要な部分です。NetNode インスタンスは、完全な接続オブジェクトを表します。NetNode に基づいて、簡単に拡張できます。主な役割は次のとおりです。 接続の維持
データ転送
データ受信
インターフェース表示
次に、ネットワーク関連のメンバー関数を紹介します。まず、初期化を見てみましょう。
onConnected メソッドは、ネットワーク接続が成功した後に呼び出され、認証プロセスが自動的に開始されます (_connectedCallback が設定されている場合)。認証が完了したら、onChecked メソッドを呼び出して NetNode を通信可能な状態にする必要があります。認証されていない場合は、ビジネス リクエストを送信しないでください。ただし、ログイン検証などのリクエストはサーバーに送信する必要があります。このようなリクエストは、force パラメーターを使用してサーバーに強制的に送信できます。 何らかのメッセージを受信すると、onMessage がトリガーされます。まず、データ パケットが検証されます。検証ルールは、独自の ProtocolHelper に実装できます。正当なデータ パケットである場合は、ハートビート タイマーとタイムアウト タイマー (re-time) を更新し、最後に _requests と _listener でメッセージの処理関数を見つけます。ここでは、rspCmd を検索します。rspCmd は、ProtocolHelper の getPackageId から取得されます。プロトコルのコマンドまたはシーケンス番号を返すことができ、リクエストと応答がどのように対応するかを決定できます。 onError と onClosed はネットワークが失敗して閉じられたときに呼び出されます。エラーの有無に関わらず、最終的には onClosed が呼び出されます。ここでは切断コールバックを実行し、自動再接続処理を行います。もちろん、close を呼び出してソケットを閉じることもできます。 close と closeSocket の違いは、closeSocket はソケットを閉じるだけであることです。私は引き続き現在の NetNode を使用し、次の接続でネットワークを復元したいと考えています。そして、close はすべての状態をクリアします。 ネットワーク要求を開始するには3 つの方法があります。 send メソッドは単にデータを送信します。ネットワークが現在切断されているか検証が進行中の場合は、_request キューに入ります。 リクエストメソッドは、リクエストを行う際に、クロージャの形式でコールバックを渡します。コールバックは、リクエストに対する応答が返ってきたときに実行されます。同時に複数の同一リクエストがある場合、これらの N 個のリクエストの応答が順番にクライアントに返され、応答コールバックも順番に実行されます (一度に実行されるコールバックは 1 つだけです)。 requestUniqueメソッドでは、複数の同一リクエストを発行したくない場合は、requestUnique を使用して、各タイプのリクエストが同時に 1 つだけになるようにすることができます。 ここで重複がないようにトラバーサル _requests を使用する理由は、_requests に大量のリクエストが蓄積されることがなく、タイムアウトや異常な再送信によって _requests のバックログが発生しないためです。再送信ロジックは NetNode によって制御され、ネットワークが切断されたときに、ユーザーがリクエストを開始するのをブロックする必要があります。このとき、通常はフルスクリーン マスク (ネットワークの変動などのプロンプト) が表示されます。 コールバックには 2 種類あります。1 つは、前述のリクエスト コールバックです。このコールバックは一時的なもので、通常はリクエスト レスポンス実行ですぐにクリーンアップされます。_listener コールバックは永続的なもので、特定のインターフェイスを開くときにリッスンしたり、終了するときに閉じたり、ゲームの開始時にリッスンしたりするなど、手動で管理する必要があります。サーバーからのアクティブなプッシュ メッセージを処理するのに適しています。 最後に、ハートビートとタイムアウトに関連するタイマーがあります。_heartTimeごとにハートビートパケットを送信し、_receiveTimeごとにサーバーから返されたパケットを受信しない場合は、ネットワークが切断されていると判断します。 完全なコードについては、ソースコードを入力して表示できます。 ネットマネージャーNetManager は NetNode を管理するために使用されます。複数の異なる接続オブジェクトをサポートする必要がある可能性があるため、NetNode を管理するには NetManager が必要です。同時に、シングルトンとして、NetManager はネットワークの呼び出しも容易にします。 エクスポートクラスNetManager{ プライベート静的_instance: NetManager = null; 保護された_channels: { [キー: 数字]: ネットノード } = {}; パブリック静的getInstance(): NetManager { if (this._instance == null) { this._instance = 新しい NetManager(); } this._instance を返します。 } // ノードを追加し、ChannelID を返す パブリックsetNetNode(newNode: NetNode, チャネルID: 数値 = 0) { this._channels[チャンネルID] = newNode; } // ノードを削除 パブリックremoveNetNode(チャネルID: 数値) { this._channels[channelId]を削除します。 } // ノード接続を呼び出す public connect(options: NetConnectOptions, channelId: number = 0): boolean { if (this._channels[チャンネルID]) { this._channels[channelId].connect(options) を返します。 } false を返します。 } // Node を呼び出して送信します public send(buf: NetData, force: boolean = false, channelId: number = 0): boolean { ノードを this._channels[channelId] とします。 if (ノード) { node.send(buf, force) を返します。 } false を返します。 } // リクエストを開始し、結果が返されたときに指定されたコールバック関数を呼び出します public request(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force: boolean = false、チャネルID: 番号 = 0) { ノードを this._channels[channelId] とします。 if (ノード) { node.request(buf、rspCmd、rspObject、showTips、force); } } // request と同じですが、リクエストする前に、まずキューに rspCmd がすでに存在するかどうかを判断します。重複がある場合は、直接返されます public requestUnique(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force: ブール値 = false、チャネルID: 数値 = 0): ブール値 { ノードを this._channels[channelId] とします。 if (ノード) { node.requestUnique(buf、rspCmd、rspObject、showTips、force) を返します。 } false を返します。 } // Node を呼び出して閉じる public close(code ? : number, reason ? : string, channelId: number = 0) { if (this._channels[チャンネルID]) { this._channels[channelId].closeSocket(code, reason); を返します。 } } テスト例次に、簡単な例を使用して、ネットワーク フレームワークの基本的な使用方法を説明します。まず、次の図に示すように、表示用の簡単なインターフェイス、3 つのボタン (接続、送信、閉じる)、2 つの入力ボックス (URL を入力、送信するコンテンツを入力)、およびテキスト ボックス (サーバーから受信したデータを表示) を組み立てる必要があります。
次に、単純なコンポーネントを実装します。ここで、新しい NetExample.ts ファイルが作成されます。タスクは非常に単純です。初期化中に、NetNode が作成され、デフォルトの受信コールバックがバインドされます。受信コールバックでは、サーバーから返されたテキストが msgLabel に表示されます。次は、接続、送信、終了のためのいくつかのインターフェースの実装です。 // 重要でないコードは省略 @ccclassexport デフォルトクラス NetExample は cc.Component を拡張します { @property(cc.ラベル) テキストラベル: cc.Label = null; @property(cc.ラベル) urlLabel: cc.Label = null; @property(cc.リッチテキスト) msgLabel: cc.RichText = null; プライベートラインカウント: 数値 = 0; オンロード() { Node = new NetNode(); Node.init(新しい WebSock()、新しい DefStringProtocol()); Node.setResponeHandler(0, (cmd: 数値, データ: NetData) => { (this.lineCount > 5)の場合{ idx = this.msgLabel.string.search("\n"); とします。 this.msgLabel.string = this.msgLabel.string.substr(idx + 1); } this.msgLabel.string += `${data}\n`; ++this.lineCount; }); NetManager.getInstance().setNetNode(ノード); } onConnectClick() { NetManager.getInstance().connect({ url: this.urlLabel.string }); } onSendClick() { NetManager.getInstance().send(this.textLabel.string); } onDisconnectClick() { NetManager.getInstance().close(); } } コードが完成したら、それをシーンの Canvas ノードの下にマウントし (他のノードでも OK)、シーン内の Label と RichText を NetExample のプロパティ パネルにドラッグします。 実行効果は次のとおりです。 まとめご覧のとおり、Websocket の使用は非常に簡単です。開発プロセスでは、さまざまな要件や問題に遭遇します。適切な設計を実装し、問題を迅速に解決する必要があります。 一方で、使用するテクノロジーを深く理解する必要があります。Websocket の基盤となるプロトコル伝送はどのように実装されているのでしょうか? tcp と http の違いは何ですか? Websocket ベースの送信に UDP を使用できますか? Websocket を使用してデータを送信する場合、データ ストリームを自分でサブパケット化する必要がありますか (Websocket プロトコルはパケットの整合性を保証します)?データ送信時に送信バッファが蓄積されていますか (bufferedAmount を確認してください)? さらに、使用シナリオとニーズを理解する必要があります。ニーズを徹底的に理解すればするほど、デザインは良くなります。どの要件がプロジェクト固有で、どの要件が汎用的ですか?共通要件は必須ですか、それともオプションですか?さまざまな変更をクラスまたはインターフェースにカプセル化し、ポリモーフィズムを使用して実装する必要がありますか?または構成を提供しますか?コールバックバインディング?イベント通知? 深い経験を積み、効率を向上させるためには、次のプロジェクトに適した適切なフレームワークを設計し、各プロジェクトの反復を最適化する必要があります。 上記は、CocosCreator の一般的なフレームワーク設計のネットワークの詳細な内容です。CocosCreator のフレームワーク設計のネットワークの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: 時間のかかるMySQLレコードのSQL例の詳細な説明
>>: autoconfを使用してMakefileを生成し、プロジェクトをコンパイルする手順
Docker コンテナはさまざまな方法で管理およびデプロイできます。 Docker コマンドを直接使...
以前は、角を丸くするのは非常に面倒でしたが、CSS3 では、角を丸くするのは非常に簡単になり、bor...
1. Vue レスポンシブの使用法を確認する Vue の応答性は、私たち全員がよく知っています。 ...
背景SQL クエリを実行するときに、where 条件の vachar 型フィールドの単一引用符を削除...
通常、すべての Web サイトは、多くの非検索エンジン クローラーに遭遇します。これらのクローラーの...
目次序文1. ロックとは何ですか? 2. InnoDBストレージエンジンのロック2.1 ロックの種類...
この記事の例では、参考までに簡単な計算機を実装するためのJavaScriptの具体的なコードを共有し...
現在このような問題が発生しています 私の状況は、QT が動かなくなってしまったため、仮想マシンを再起...
質問画像とテキストのシームレスなスクロールは、一般的に携帯電話では良い効果をもたらしますが、一部のモ...
NTP は、ネットワーク上で時間を同期するための TCP/IP プロトコルです。通常、クライアントは...
Linux システムは典型的なマルチユーザー システムです。異なるユーザーは異なる立場にあり、異なる...
クローラーの開発プロセス中に、クローラーを複数のサーバーに展開する必要がある状況に遭遇したことがある...
1. ダウンロードして解凍します: /Users/xiechunping/Softwares/mys...
インターネット上には Linux サーバーを監視するためのツール、コンポーネント、プログラムが多数あ...
仮想マシンは非常に便利なテストソフトウェアです。ハードウェアに損傷を与えることなく、さまざまなテスト...