国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - js教程 - CocosCreator通用框架設計之網絡

CocosCreator通用框架設計之網絡

2022-03-01 16:38weixin_39752434 js教程

這篇文章主要介紹了CocosCreator通用框架設計之網絡,詳細講解了WebSocket的原理和使用方法,對WebSocket感興趣的同學,一定要看一下

前言

在 Cocos Creator 中發起一個 http 請求是比較簡單的,但很多游戲希望能夠和服務器之間保持長連接,以便服務端能夠主動向客戶端推送消息,而非總是由客戶端發起請求,對于實時性要求較高的游戲更是如此。這里我們會設計一個通用的網絡框架,可以方便地應用于我們的項目中。

使用websocket

在實現這個網絡框架之前,我們先了解一下 websocket。websocket 是一種基于 tcp 的全雙工網絡協議,可以讓網頁創建持久性的連接,進行雙向的通訊。在 Cocos Creator 中使用 websocket 既可以用于 H5 網頁游戲上,同樣支持原生平臺 Android 和 iOS。

構造 websocket 對象

在使用 websocket 時,第一步應該創建一個 websocket 對象。websocket 對象的構造函數可以傳入2個參數,第一個是 url 字符串,第二個是協議字符串或字符串數組,指定了可接受的子協議,服務端需要選擇其中的一個返回,才會建立連接,但我們一般用不到。

url 參數非常重要,主要分為4部分:協議、地址、端口、資源。

比如 ws://echo.websocket.org:

協議:必選項,默認是 ws 協議,如果需要安全加密則使用 wss。 地址:必選項,可以是 ip 或域名,當然建議使用域名。 端口:可選項,在不指定的情況下,ws 的默認端口為 80,wss 的默認端口為 443。 資源:可選性,一般是跟在域名后某資源路徑,我們基本不需要它。

websocket 的狀態

websocket 有4個狀態,可以通過 readyState 屬性查詢:

0 CONNECTING 尚未建立連接。 1 OPEN WebSocket連接已建立,可以進行通信。 2 CLOSING 連接正在進行關閉握手,或者該close()方法已被調用。 3 CLOSED 連接已關閉。

websocket 的 API

websocket 只有2個 API,void send( data ) 發送數據和 void close( code, reason ) 關閉連接。

send 方法只接收一個參數——即要發送的數據,類型可以是以下4個類型的任意一種:string | ArrayBufferLike | Blob | ArrayBufferView。

如果要發送的數據是二進制,我們可以通過 websocket 對象的 binaryType 屬性來指定二進制的類型,binaryType 只可以被設置為“blob”或“arraybuffer”,默認為“blob”。如果我們要傳輸的是文件這樣較為固定的、用于寫入到磁盤的數據,使用 blob。而你希望傳輸的對象在內存中進行處理則使用較為靈活的 arraybuffer。如果要從其他非 blob 對象和數據構造一個 blob,需要使用 blob 的構造函數。

在發送數據時,官方有2個建議:

檢測 websocket 對象的 readyState 是否為 OPEN,是才進行 send。 檢測 websocket 對象的 bufferedAmount 是否為0,是才進行 send(為了避免消息堆積,該屬性表示調用 send 后堆積在 websocket 緩沖區的還未真正發送出去的數據長度)。

close 方法接收2個可選的參數,code 表示錯誤碼,我們應該傳入 1000 或 3000~4999 之間的整數,reason 可以用于表示關閉的原因,長度不可超過 123 字節。

websocket 的回調

websocket 提供了4個回調函數供我們綁定:

onopen:連接成功后調用。 onmessage:有消息過來時調用:傳入的對象有 data 屬性,可能是字符串、blob 或 arraybuffer。 onerror:出現網絡錯誤時調用:傳入的對象有 data 屬性,通常是錯誤描述的字符串。 onclose:連接關閉時調用:傳入的對象有 code、reason、wasClean 等屬性。

注意:當網絡出錯時,會先調用 onerror 再調用 onclose,無論何種原因的連接關閉,onclose 都會被調用。

Echo 實例

下面 websocket 官網的 echo demo 的代碼,可以將其寫入一個 html 文件中并用瀏覽器打開,打開后會自動創建 websocket 連接,在連接上時主動發送了一條消息“WebSocket rocks”,服務器會將該消息返回,觸發 onMessage,將信息打印到屏幕上,然后關閉連接。具體可以參考:http://www.websocket.org/echo.html17 

默認的 url 前綴是wss,由于 wss 抽風,使用 ws 才可以連接上,如果 ws 也抽風,可以試試連這個地址ws://121.40.165.18:8800,這是國內的一個免費測試 websocket 的網址。

 設計框架

一個通用的網絡框架,在通用的前提下還需要能夠支持各種項目的差異需求,根據經驗,常見的需求差異如下:

用戶協議差異,游戲可能傳輸 json、protobuf、flatbuffer 或者自定義的二進制協議。 底層協議差異,我們可能使用 websocket、或者微信小游戲的 wx.websocket、甚至在原生平臺我們希望使用 tcp/udp/kcp 等協議。 登陸認證流程,在使用長連接之前我們理應進行登陸認證,而不同游戲登陸認證的方式不同。 網絡異常處理,比如超時時間是多久,超時后的表現是怎樣的,請求時是否應該屏蔽 UI 等待服務器響應,網絡斷開后表現如何,自動重連還是由玩家點擊重連按鈕進行重連,重連之后是否重發斷網期間的消息?等等這些。 多連接的處理,某些游戲可能需要支持多個不同的連接,一般不會超過2個,比如一個主連接負責處理大廳等業務消息,一個戰斗連接直接連戰斗服務器,或者連接聊天服務器。

根據上面的這些需求,我們對功能模塊進行拆分,盡量保證模塊的高內聚,低耦合。

CocosCreator通用框架設計之網絡

ProtocolHelper 協議處理模塊——當我們拿到一塊 buffer時,我們可能需要知道這個 buffer 對應的協議或者 id 是多少,比如我們在請求的時候就傳入了響應的處理回調,那么常用的做法可能會用一個自增的 id 來區別每一個請求,或者是用協議號來區分不同的請求,這些是開發者需要實現的。我們還需要從 buffer 中獲取包的長度是多少?包長的合理范圍是多少?心跳包長什么樣子等等。

Socket 模塊——實現最基礎的通訊功能,首先定義 Socket 的接口類 ISocket,定義如連接、關閉、數據接收與發送等接口,然后子類繼承并實現這些接口。

NetworkTips 網絡顯示模塊——實現如連接中、重連中、加載中、網絡斷開等狀態的顯示,以及 UI 的屏蔽。

NetNode 網絡節點——所謂網絡節點,其實主要的職責是將上面的功能串聯起來,為用戶提供一個易用的接口。

NetManager 管理網絡節點的單例——我們可能有多個網絡節點(多條連接),所以這里使用單例來進行管理,使用單例來操作網絡節點也會更加方便。

ProtocolHelper

在這里定義了一個 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或協議類型
}

Socket

在這里定義了一個 ISocket 的簡單接口,如下所示:

// Socket接口
export interface ISocket {    
    onConnected: (event) => void;                 //連接回調 
    onMessage: (msg: NetData) => void;            // 消息回調    
    onError: (event) => void;                     // 錯誤回調    
    onClosed: (event) => void;                    // 關閉回調    
    connect(ip: string, port: number);            // 連接接口    
    send(buffer: NetData);                        // 數據發送接口    
    close(code?: number, reason?: string);        // 關閉接口
}

接下來我們實現一個 WebSock,繼承于 ISocket,我們只需要實現 connect、send 和 close 接口即可。send 和 close 都是對 websocket 對簡單封裝,connect 則需要根據傳入的 ip、端口等參數構造一個 url 來創建 websocket,并綁定 websocket 的回調。

export class WebSock implements ISocket {    
    private _ws: WebSocket = null;              // websocket對象   
    onConnected: (event) => void = null;    
    onMessage: (msg) => void = null;    
    onError: (event) => void = null;    
    onClosed: (event) => void = null;   
    connect(options: any) {        
    if (this._ws) {            
        if (this._ws.readyState === WebSocket.CONNECTING) {                
            console.log("websocket connecting, wait for a moment...")                
            return false;
        }
    }
    let url = null;        
    if(options.url) {           
        url = options.url;        
    } else {            
        let ip = options.ip;            
        let port = options.port;            
        let protocol = options.protocol;            
        url = `${protocol}://${ip}:${port}`;           
    }        
        this._ws = new WebSocket(url);       
        this._ws.binaryType = options.binaryType ? options.binaryType : "arraybuffer";        
        this._ws.onmessage = (event) => {           
        this.onMessage(event.data);        
    };        
        this._ws.onopen = this.onConnected;        
        this._ws.onerror = this.onError;        
        this._ws.onclose = this.onClosed;       
        return true;    
    }    
    send(buffer: NetData) {        
    if (this._ws.readyState == WebSocket.OPEN)  {           
        this._ws.send(buffer);           
        return true;       
    }       
    return false;    
    }    
    close(code?: number, reason?: string) {        
    this._ws.close();    
    }
}

NetworkTips

INetworkTips 提供了非常的接口,重連和請求的開關,框架會在合適的時機調用它們,我們可以繼承 INetworkTips 并定制我們的網絡相關提示信息,需要注意的是這些接口可能會被**多次調用**。

// 網絡提示接口
export interface INetworkTips {    
    connectTips(isShow: boolean): void;    
    reconnectTips(isShow: boolean): void;    
    requestTips(isShow: boolean): void;
}

NetNode

NetNode 是整個網絡框架中最為關鍵的部分,一個 NetNode 實例表示一個完整的連接對象,基于 NetNode 我們可以方便地進行擴展,它的主要職責有:

連接維護

連接的建立與鑒權(是否鑒權、如何鑒權由用戶的回調決定) 斷線重連后的數據重發處理 心跳機制確保連接有效(心跳包間隔由配置,心跳包的內容由ProtocolHelper定義) 連接的關閉

數據發送

支持斷線重傳,超時重傳 支持唯一發送(避免同一時間重復發送)

數據接收

支持持續監聽 支持request-respone模式

界面展示

可自定義網絡延遲、短線重連等狀態的表現 首先我們定義了 NetTipsType、NetNodeState 兩個枚舉,以及 NetConnectOptions 結構供 NetNode 使用。 接下來是 NetNode 的成員變量,NetNode 的變量可以分為以下幾類: NetNode 自身的狀態變量,如 ISocket 對象、當前狀態、連接參數等等。 各種回調,包括連接、斷開連接、協議處理、網絡提示等回調。 各種定時器,如心跳、重連相關的定時器。 請求列表與監聽列表,都是用于接收到的消息處理。

接下來介紹網絡相關的成員函數,首先看初始化與:

init 方法用于初始化 NetNode,主要是指定 Socket 與協議等處理對象。 connect 方法用于連接服務器。 initSocket 方法用于綁定 Socket 的回調到 NetNode 中。 updateNetTips 方法用于刷服務器之家絡提示。

onConnected 方法在網絡連接成功后調用,自動進入鑒權流程(如果設置了_connectedCallback),在鑒權完成后需要調用 onChecked 方法使 NetNode 進入可通訊的狀態,在未鑒權的情況,我們不應該發送任何業務請求,但登錄驗證這類請求應該發送給服務器,這類請求可以通過帶force參數強制發送給服務器。

接收到任何消息都會觸發 onMessage,首先會對數據包進行校驗,校驗的規則可以在自己的 ProtocolHelper 中實現,如果是一個合法的數據包,我們會將心跳和超時計時器進行更新——重新計時,最后在 _requests 和 _listener 中找到該消息的處理函數,這里是通過 rspCmd 進行查找的,rspCmd 是從 ProtocolHelper 的 getPackageId 取出的,我們可以將協議的命令或者序號返回,由我們自己來決定請求和響應如何對應。

onError 和 onClosed 是網絡出錯和關閉時調用的,無論是否出錯,最終都會調用 onClosed,在這里我們執行斷線回調,以及做自動重連的處理。當然也可以調用 close來關閉套接字。close 與 closeSocket 的區別在于 closeSocket 只是關閉套接字——我仍然要使用當前的 NetNode,可能通過下一次 connect 恢復網絡。而 close則是清除所有的狀態。

發起網絡請求有3種方式:

send 方法,純粹地發送數據,如果當前斷網或者驗證中會進入 _request 隊列。

request 方法,在請求的時候即以閉包的方式傳入回調,在該請求的響應回到時會執行回調,如果同時有多個相同的請求,那么這 N 個請求的響應會依次回到客戶端,響應回調也會依次執行(每次只會執行一個回調)。

requestUnique 方法,如果我們不希望有多個相同的請求,可以使用 requestUnique 來確保每一種請求同時只會有一個。

這里確保沒有重復之所以使用的是遍歷 _requests,是因為我們不會積壓大量的請求到 _requests中,超時或異常重發也不會導致 _requests 的積壓,因為重發的邏輯是由 NetNode 控制的,而且在網絡斷開的情況下,我們理應屏蔽用戶發起請求,此時一般會有一個全屏遮罩——網絡出現波動之類的提示。

我們有2種回調,一種是前面的 request 回調,這種回調是臨時性的,一般隨著請求-響應-執行而立即清理,_listener 回調則是常駐的,需要我們手動管理的,比如打開某界面時監聽、離開是關閉,或者在游戲一開始就進行監聽。適合處理服務器的主動推送消息。

最后是心跳與超時相關的定時器,我們每隔 _heartTime 會發送一個心跳包,每隔 _receiveTime 檢測如果沒有收到服務器返回的包,則判斷網絡斷開。

完整代碼,大家可以進入源碼查看!

NetManager

NetManager 用于管理 NetNode,這是由于我們可能需要支持多個不同的連接對象,所以需要一個 NetManager 專門來管理 NetNode,同時,NetManager 作為一個單例,也可以方便我們調用網絡。

export class NetManager {
	private static _instance: NetManager = null;
	protected _channels: {
		[key: number]: NetNode
	} = {};
	public static getInstance(): NetManager {
		if (this._instance == null) {
			this._instance = new NetManager();
		}
		return this._instance;
	} // 添加Node,返回ChannelID    
	public setNetNode(newNode: NetNode, channelId: number = 0) {
		this._channels[channelId] = newNode;
	} // 移除Node    
	public removeNetNode(channelId: number) {
		delete this._channels[channelId];
	} // 調用Node連接    
	public connect(options: NetConnectOptions, channelId: number = 0): boolean {
		if (this._channels[channelId]) {
			return this._channels[channelId].connect(options);
		}
		return false;
	} // 調用Node發送   
	public send(buf: NetData, force: boolean = false, channelId: number = 0): boolean {
		let node = this._channels[channelId];
		if (node) {
			return node.send(buf, force);
		}
		return false;
	} // 發起請求,并在在結果返回時調用指定好的回調函數   
	public request(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force: boolean =
		false, channelId: number = 0) {
		let node = this._channels[channelId];
		if (node) {
			node.request(buf, rspCmd, rspObject, showTips, force);
		}
	} // 同request,但在request之前會先判斷隊列中是否已有rspCmd,如有重復的則直接返回    
	public requestUnique(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force:
		boolean = false, channelId: number = 0): boolean {
		let node = this._channels[channelId];
		if (node) {
			return node.requestUnique(buf, rspCmd, rspObject, showTips, force);
		}
		return false;
	} // 調用Node關閉  
	public close(code ? : number, reason ? : string, channelId: number = 0) {
		if (this._channels[channelId]) {
			return this._channels[channelId].closeSocket(code, reason);
		}
	}

測試例子

接下來我們用一個簡單的例子來演示一下網絡框架的基本使用,首先我們需要拼一個簡單的界面用于展示,3個按鈕(連接、發送、關閉),2個輸入框(輸入 url、輸入要發送的內容),一個文本框(顯示從服務器接收到的數據),如下圖所示。

該例子連接的是 websocket 官方的 echo.websocket.org 地址,這個服務器會將我們發送給它的所有消息都原樣返回給我們。

CocosCreator通用框架設計之網絡

接下來,實現一個簡單的 Component,這里新建了一個 NetExample.ts 文件,做的事情非常簡單,在初始化的時候創建 NetNode、綁定默認接收回調,在接收回調中將服務器返回的文本顯示到 msgLabel中。接著是連接、發送和關閉幾個接口的實現:

// 不關鍵的代碼省略
@ccclassexport
default class NetExample extends cc.Component {
	@property(cc.Label)
	textLabel: cc.Label = null;
	@property(cc.Label)
	urlLabel: cc.Label = null;
	@property(cc.RichText)
	msgLabel: cc.RichText = null;
	private lineCount: number = 0;
	onLoad() {
		let Node = new NetNode();
		Node.init(new WebSock(), new DefStringProtocol());
		Node.setResponeHandler(0, (cmd: number, data: NetData) => {
			if (this.lineCount > 5) {
				let 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(Node);
	}
	onConnectClick() {
		NetManager.getInstance().connect({
			url: this.urlLabel.string
		});
	}
	onSendClick() {
		NetManager.getInstance().send(this.textLabel.string);
	}
	onDisconnectClick() {
		NetManager.getInstance().close();
	}
}

代碼完成后,將其掛載到場景的 Canvas 節點下(其他節點也可以),然后將場景中的 Label 和 RichText 拖拽到我們的 NetExample 的屬性面板中:

CocosCreator通用框架設計之網絡

運行效果如下所示:

CocosCreator通用框架設計之網絡

小結

可以看到,Websocket 的使用很簡單,我們在開發的過程中會碰到各種各樣的需求和問題,要實現一個好的設計,快速地解決問題。

我們一方面需要對我們使用的技術本身有深入的理解,websocket 的底層協議傳輸是如何實現的?與 tcp、http 的區別在哪里?基于 websocket 能否使用 udp 進行傳輸呢?使用 websocket 發送數據是否需要自己對數據流進行分包(websocket 協議保證了包的完整)?數據的發送是否出現了發送緩存的堆積(查看 bufferedAmount)?

另外需要對我們的使用場景及需求本身的理解,對需求的理解越透徹,越能做出好的設計。哪些需求是項目相關的,哪些需求是通用的?通用的需求是必須的還是可選的?不同的變化我們應該封裝成類或接口,使用多態的方式來實現呢?還是提供配置?回調綁定?事件通知?

我們需要設計出一個好的框架,來適用于下一個項目,并且在一個一個的項目中優化迭代,這樣才能建立深厚的沉淀、提高效率。

以上就是CocosCreator通用框架設計之網絡的詳細內容,更多關于CocosCreator框架設計之網絡的資料請關注服務器之家其它相關文章!

原文鏈接:https://blog.csdn.net/weixin_39752434/article/details/111206537

延伸 · 閱讀

精彩推薦
  • js教程選擇 JavaScript 測試框架的標準

    選擇 JavaScript 測試框架的標準

    由于 JavaScript 被廣泛認為是“web語言”,因此該語言的測試自動化框架是最豐富和最受歡迎的也就不足為奇了。通過考慮不同框架的屬性,你將更加清楚哪...

    粵嵌教育6982022-01-07
  • js教程原生js實現星星閃爍效果

    原生js實現星星閃爍效果

    這篇文章主要為大家詳細介紹了原生js實現星星閃爍效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    會做飯的技術男11242022-02-12
  • js教程JavaScript實現動態加載刪除表格

    JavaScript實現動態加載刪除表格

    這篇文章主要為大家詳細介紹了JavaScript實現動態加載刪除表格,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    KathyLJQ7432022-02-27
  • js教程JavaScript中展開運算符及應用的實例代碼

    JavaScript中展開運算符及應用的實例代碼

    這篇文章主要介紹了JavaScript中展開運算符及應用的實例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以...

    banana peel9662021-12-31
  • js教程一篇帶你快速上手 Esbuild

    一篇帶你快速上手 Esbuild

    為什么 Vite 那么快呢?除了使用了 ES modules 之外,Vite 內部還使用了一個神器— esbuild。...

    全棧修仙之路6372022-02-24
  • js教程基于javascript實現移動端輪播圖效果

    基于javascript實現移動端輪播圖效果

    這篇文章主要為大家詳細介紹了基于javascript實現移動端輪播圖效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    A.香辣雞腿堡9062021-12-15
  • js教程原生js拖拽功能制作滑動條實例代碼

    原生js拖拽功能制作滑動條實例代碼

    這篇文章主要介紹了原生js拖拽功能制作滑動條實例教程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的...

    蔣偉平3522022-01-17
  • js教程js實現拖拽拼圖游戲

    js實現拖拽拼圖游戲

    這篇文章主要為大家詳細介紹了js實現拖拽拼圖游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    一個學前端的小菜鳥11652022-01-12
主站蜘蛛池模板: 国产在线精品一区二区 | 久久婷婷欧美 | 欧美精产国品一二三区 | 综合五月 | 日韩五月| 亚洲性视频在线 | 久久久亚洲 | 欧美精品第一页 | 在线视频一区二区 | 国产日韩精品久久 | 色婷婷国产精品综合在线观看 | 日本高清无卡码一区二区久久 | 国产精品美女久久久久久久久久久 | 久久久免费电影 | 欧美激情一区二区三区 | 国产一区自拍视频 | 欧州一区二区三区 | 欧美高清在线 | 亚洲国产精品一区二区三区 | 成人午夜精品一区二区三区 | av手机在线电影 | 日韩精品在线视频观看 | 国产精品一区二区久久 | 伊人99| 亚洲综合色自拍一区 | 激情综合色综合久久综合 | 欧美一区三区 | 久久久精品视频网站 | 在线亚洲精品 | 久久精品 | 岛国一区 | 午夜精品一区二区三区在线播放 | 不卡久久 | av一二三区 | 亚洲国产成人91精品 | 秋霞精品 | 国产亚洲精品久久久久久 | 精品成人18 | 日韩不卡一二三 | 这里只有久久精品 | 亚洲热妇|