深入了解使用egret.WebSocket
概念
本教程不讲解TCP/IP协议,Socket属于哪层,消息包体怎么设计等,主讲 egret.WebSocket 使用示例 与 protobuf 使用示例。
在使用egret.WebSocket之前需要简单讨论了解目前几种通信模式。
HTTP
网站中常见的一种传输协议,用于访问页面或资源时,向页面所在的服务器发送一个 HTTP 请求。服务器识别请求,返回响应数据并关闭连接。这过程中客户端不请求,服务器不能主动推送消息到客户端。早些的游戏通过轮训以及 AJAX 实现了不需要手动刷新程序内部轮训请求的伪的长连接。这显然是一个非常不明智的方式。可以想象一下聊天室或人物移动场景中,如果我们使用 HTTP 会是一种什么情况。大量的请求与响应报头额外的数据、延迟不断发生、传输带宽压力不断增加,这对于ARPG等类型游戏是致命的。主要适合对即时性要求不高的游戏类型。
Socket
端游中常见的一种传输协议,套链接。需要了解 Socket 的同学百度一下,它是一个长连接的协议。在完成握手后,连接会一直开着,直到客户端或服务器明确予以关闭。在这过程中,服务器能主动的推送消息到客户端,消息格式可以是进制流以及自定义格式等。后期由于FLASH的兴起,页游中绝大多数都在使用。可以想象一下聊天室或人物移动场景中,我们使用 socket 会是一种什么情况。没有额外的数据、主动的消息推送、低延迟等等。
WebScoket
早期 HTML 中并没有提供 socket 的支持,大型页游项目依靠于 Flash 提供的 Socket API 。随着 HTML5 的制定与完善,WebSocket 被各大浏览器厂商所支持。
WebScoket 与 Socket 的区别在于前者提供了完善的API以及握手的机制,而后者是抽象出来的一种概念,具体的实现对于各种语言都可能不同,例如:我们需要自定义协议体,控制缓存区,连接确认方式等。而在 WebSocket 中,每个消息的传输规范都是定义好的,如消息以 0x00 字节开头,以 0xff 结尾,中间数据采用 UTF-8 编码格式,第一次握手必须使用 ws://xxx 或 wss://xxx 进行,在握手成功后将协议升级为 WebSocket 协议,进行双工的通信。第一次请求走的是 HTTP 请求。由于各种规范的定义与实现,旧有的服务器 Socket 并不适用于 WebSocket 。
实际上,许多语言、框架和服务器都提供了 WebSocket 支持,例如:
- 基于 C 的 libwebsocket.org
- 基于 Node.js 的 Socket.io
- 基于 Python 的 ws4py
- 基于 C++ 的 WebSocket++
- Apache 对 WebSocket 的支持:Apache Module mod_proxy_wstunnel
- Nginx 对 WebSockets 的支持: NGINX as a WebSockets Proxy 、 NGINX Announces Support for WebSocket Protocol 、WebSocket proxying
- lighttpd 对 WebSocket 的支持:mod_websocket
egret.WebSocket 使用示例
早期参与或制作游戏项目,对下图一定不陌生,定义消息长度位、消息号以及消息读取规范,客户端根据协议规范以字节形式读取包体:
HML5 的 WebSocket 传输中,并没有定义进制流的传送读取。 egret.WebSocket 中对 HTML5 中 WebSocket 进行封装,实现了对于进制流的传输。
egret.WebSocket 默认是字符串形式接受数据,创建一个 egret.WebSocket 非常简单,由于 egret.WebSocket 对字节流传输的实现,服务器与客户端旧有的协议非常方便移植。以下示例演示了创建 egret.WebSocket :
1.修改项目文件 egretProperties.json 中的 modules ,增加 {"name": "socket"}
2.在项目所在目录执行一次编译引擎 egret build -e
this.socket = new egret.WebSocket();
//设置数据格式为二进制,默认为字符串
this.socket.type = egret.WebSocket.TYPE_BINARY;
//添加收到数据侦听,收到数据会调用此方法
this.socket.addEventListener(egret.ProgressEvent.SOCKET_DATA, this.onReceiveMessage, this);
//添加链接打开侦听,连接成功会调用此方法
this.socket.addEventListener(egret.Event.CONNECT, this.onSocketOpen, this);
//添加链接关闭侦听,手动关闭或者服务器关闭连接会调用此方法
this.socket.addEventListener(egret.Event.CLOSE, this.onSocketClose, this);
//添加异常侦听,出现异常会调用此方法
this.socket.addEventListener(egret.IOErrorEvent.IO_ERROR, this.onSocketError, this);
//连接服务器
this.socket.connect("echo.websocket.org", 80);
当触发 egret.Event.CONNECT 侦听方法 onSocketOpen 时连接服务器成功,可以进行数据发送接收。我们创建一个字节数组,通过writeType写入字符串类型,布尔类型,整形,设置指针为开始0,调用 this.socket.writeBytes 写入数据进行数据发送:
var byte:egret.ByteArray = new egret.ByteArray();
byte.writeUTF("Hello Egret WebSocket");
byte.writeBoolean(false);
byte.writeInt(123);
byte.position = 0;
this.socket.writeBytes(byte, 0, byte.bytesAvailable);
this.socket.flush();
当触发 egret.ProgressEvent.SOCKET_DATA 侦听方法 onReceiveMessage() 时数据接收成功,创建一个字节数组并将 socket 中当前数据读入其中,与发送方式类似,接收使用 readType :
var byte:egret.ByteArray = new egret.ByteArray();
this.socket.readBytes(byte);
var msg:string = byte.readUTF();
var boo:boolean = byte.readBoolean();
var num:number = byte.readInt();
protobuf 使用示例
百度百科 protocolbuffer 介绍,protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
protobuf 被适用于非常多的生产环境中,也出现了各种语言的版本,方便了数据的移植与可维护性。它在部分语言项目中有一定缺陷,如随着项目的不断迭代会产生较多的数据结构类机器码增加项目体积。
这里以第三库的形式加入对 protobufjs 的支持。想了解第三方集成的同学点击:集成第三方JavaScript库
示例下载见教程尾部:
1.拷贝示例项目 libs 目录下 protobuf 目录到新项目所在 libs 目录。
2.拷贝 libsrc 目录下 protobuf 目录到新项目所在 protobuf 目录。
3.项目 egretProperties.json 中增加相关内容。
egretProperties.json:
{
"document_class": "Main",
"modules": [
{
"name": "core"
},
{
"name": "version"
},
{
"name": "res"
},
{
"name": "socket"
},
{
"name": "protobuf",
"path": "libsrc/protobuf"
}
],
"egret_version": "2.0.2"
}
编译引擎,完成对protobuf配置。
在 resource\assets\proto下 ,新建数据文件并命名为 common.proto 。在其中定义我们需要传输的类对象。这个文件在实际生产环境中是服务端客户端公用的,可以有单个或多个根据具体项目而定。通过工具生产对应语言的访问类,如name.ts,并引入项目中,通过 new 或其他方式创建实例。可惜的是目前还没有egret语言所使用的生成工具。
首先在 common.proto 内定义结构体,了解语法点击这里 。我们定义一个简单结构,如:
message Common {
required uint32 id = 1;
required string text = 2;
}
在 resource.js 中我们引入 common.proto 文件,为了方便,在初始化进行加载。也可以使用 RESDepot 工具进行导入。
当文件被加载后,进行数据设置之前需要四步:
1.获取资源数据文件。
2.解码并创建对象构造器。
3.创建需要的数据结构类。
4.实例化数据结构类。
设置与读取示例,如下代码:
var proto: string = RES.getRes("common_proto");
var builder:any = dcodeIO.ProtoBuf.loadProto(proto);
var clazz:any = builder.build("Common");
var data:any = new clazz();
data.set("id",1);//可以使用data.id=1;
data.set("text","oops");//可以使用data.text=oops;
console.log("id=" + data.get("id"));
console.log("oops=" + data.get("text"));
我想我写这到这里,不只是为了创建一个文件,序列化数据,反序列化数据,然后创建个实例吧。 好吧,我们继续往下讲,下面就是我们具体使用 egret.WebSocket 发送数据。 这是我们使用它的关键。
在使用上例中 builder.build("Common") 得到对象构造器中提供了序列化的方法 toArrayBuffer() 通过 egret.ByteArray 写入序列化进行传输,在实际的环境中,还需要涉及到一些长度位,校验,消息号等这里不做讨论。发送示例,如下代码:
var arraybuffer: ArrayBuffer = data.toArrayBuffer();
var len: number = arraybuffer.byteLength;
var btyearray:egret.ByteArray=new egret.ByteArray(arraybuffer);
if(len > 0)
{
this.socket.writeBytes(btyearray);
this.socket.flush();
}
接收数据, 我们代码中一直出现 ArrayBuffer 这是JS中一种用于二进制数据存储的类型,与我们的 ByteAarry 相似(ByteAarry封装了ArrayBuffer) 通过 DataView 提供的接口,转换为我们可以使用的 ByteAarray 数据,如下代码:
var msgBuff: ArrayBuffer;
var btyearray: egret.ByteArray = new egret.ByteArray();
this.socket.readBytes(btyearray);
var len = btyearray.buffer.byteLength;
var dataView = new DataView(btyearray.buffer);
var pbView = new DataView(new ArrayBuffer(len));
for(var i = 0;i < len;i++) {
pbView.setInt8(i,dataView.getInt8(i));
}
msgBuff = pbView.buffer;
var proto: string = RES.getRes("common_proto");
var builder:any = dcodeIO.ProtoBuf.loadProto(proto);
var clazz:any = builder.build("Common");
var data: any = clazz.decode(msgBuff);
console.log("decodeData id=" + data.get("id"));
console.log("decodeData oops=" + data.get("text"));
项目示例:下载
最后,感谢董刚同学提供的protobuf库。
深入了解使用egret.WebSocket的更多相关文章
- 【咸鱼教程】protobuf在websocket通讯中的使用
教程目录一 protobuf简介二 使用protobuf三 Demo下载 参考: CSDN:Egret项目中使用protobuf(protobufjs) TS项目中使用Protobuf的解决方案(ba ...
- 网络游戏开发-客户端2(自定义websocket协议格式)
Egret官方提供了一个Websocket的库,可以让我们方便的和服务器长连接交互. 标题写的时候自定义websocket的协议格式.解释一下,不是说我们去动websocket本身的东西,我们是在we ...
- 【Egret】WebSocket 的使用说明
在Egret里可以使用WebSocket ,也可以使用socket.io 首先先深入了解一下 WebSocket 在Egret里的机制,看这篇文章: 主要讲解Egret里使用WebSocket和pro ...
- Pomelo实现最简单的通信-egret。
昨天因为需要开始学习Pomelo 做H5游戏的服务端. 因为个人学习习惯,我从来不适合去跟着文档看.一般我直接是看下大概的API,但是Pomelo的API全部都是英文的. 昨天我就告诉自己用一下午时间 ...
- egret随笔-egret浅入浅出
•不知道有多人跟笔者一样,喜欢学各种技术,但是都不精,但也有一两项算是精的. 自从踏上了egret游戏开发的道路,就不得不学习各种技术了,因为,要精通egret,首先必须要会TypeScript,其次 ...
- Egret入门了解
0.前言 这个星期没有什么事做,就想找点技术了解一下.前段时间看过Egret,用来开发HTML5小游戏.一开始以为很麻烦的,但是经过这两天了解了一下,如果用这个游戏引擎来开发一些简单的游戏,还是蛮方便 ...
- egret游戏入门之学习资源篇
最近因需要,入手H5游戏. 写游戏当然需要有引擎. H5游戏开发:游戏引擎入门推荐 如何选择 H5 游戏引擎 白鹭引擎和layabox哪个好用,哪个技术更成熟 ? LayaBox 与 Egret 选择 ...
- 浅谈白鹭Egret
浅谈白鹭Egret 最近在做一个移动项目,技术选型的时候接触到了白鹭,简单了解了之后觉得挺合适的,最终就选择了这个引擎. 为什么会选择白鹭引擎呢? 我看上他主要有一下几点: 1 ...
- [egret+pomelo]实时游戏杂记(3)
[egret+pomelo]学习笔记(1) [egret+pomelo]学习笔记(2) [egret+pomelo]学习笔记(3) 服务端的请求流程走完了一遍,下面就该看一下,在目前的服务端中,各服务 ...
随机推荐
- Android数据加密解密
最近项目在维护过程中,估计这一周都会没有什么事情做了.于是开始打量自己做完的这个项目,项目在展示方面乏善可陈,然后仔细的想了想,这个项目的亮点无非就在数据加密和解密这一块了.因为是银行的项目,所以对数 ...
- iconv gbk字符转utf8字符
直接上代码 bool gbk2utf8(const char* src, char* dest, size_t inlen) { const char *inbuf = src; size_t out ...
- 数学概念——E 期望(经典问题)
E - 期望(经典问题) Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%lld & %llu Submit S ...
- Android吧数据保存成xml文件
public class MainActivity extends Activity { private List<Person> persons; @Override protected ...
- 使用Sublime Text搭建python调试环境
原文地址:http://blog.csdn.net/wenxuansoft/article/details/38559731 pycharmt等IDE虽然用着爽,但毕竟在速度.资源上还是比较让人不爽的 ...
- VirtualBox扩展Ubuntu磁盘空间
有时候我们在使用virtualBox虚拟机时,创建虚拟机时并没有考虑到所占用硬盘大小,后来可能磁盘空间不够用了. 以下方法可以帮你扩展虚拟机的存储空间(以下Ubuntu为例). 1. 在宿主机器进入V ...
- B - Frogger
题目大意: 一个叫做弗雷迪的青蛙坐在湖中间的一块石头上.突然他注意到他的青蛙女神菲奥娜坐在另一块石头上面,于是他计划去看她,但是呢湖里面的水很脏并且充满了游客的防晒霜,所以他想避免游泳而采用跳跃的方式 ...
- 10382 - Watering Grass
Problem E Watering Grass Input: standard input Output: standard output Time Limit: 3 seconds n sprin ...
- Websphere内存溢出的日志
项目中碰到Websphere内存溢出的情况.原因可能:出现过多内存泄漏,或者分配过多大内存等.解决方法:1.进入was管理控制台,选择 应用程序服务器 > server1 > 进程定义 & ...
- Spring二 Bean详解
Bean详解 Spring框架的本质其实是:通过XML配置来驱动Java代码,这样就可以把原本由java代码管理的耦合关系,提取到XML配置文件中管理.这样就实现了系统中各组件的解耦,有利于后期的升级 ...