CocosCreator通用框架设计之网络

时间:2021-05-28

前言

在 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://ponent { @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 的属性面板中:

运行效果如下所示:

小结

可以看到,Websocket 的使用很简单,我们在开发的过程中会碰到各种各样的需求和问题,要实现一个好的设计,快速地解决问题。

我们一方面需要对我们使用的技术本身有深入的理解,websocket 的底层协议传输是如何实现的?与 tcp、http 的区别在哪里?基于 websocket 能否使用 udp 进行传输呢?使用 websocket 发送数据是否需要自己对数据流进行分包(websocket 协议保证了包的完整)?数据的发送是否出现了发送缓存的堆积(查看 bufferedAmount)?

另外需要对我们的使用场景及需求本身的理解,对需求的理解越透彻,越能做出好的设计。哪些需求是项目相关的,哪些需求是通用的?通用的需求是必须的还是可选的?不同的变化我们应该封装成类或接口,使用多态的方式来实现呢?还是提供配置?回调绑定?事件通知?

我们需要设计出一个好的框架,来适用于下一个项目,并且在一个一个的项目中优化迭代,这样才能建立深厚的沉淀、提高效率。

以上就是CocosCreator通用框架设计之网络的详细内容,更多关于CocosCreator框架设计之网络的资料请关注其它相关文章!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章