|
吾爱游客
发表于 2020-8-13 09:36
申请id:black70邮箱:617389784@qq.com文章:最近看到一论坛推广自己写的网页MUD游戏,试玩一下对开发这样的游戏感到了兴趣,顺道研究一下客户端与服务器通讯上是怎么优化的。首先从 登录页面 开始,通过 Chrome 开发者工具对数据进行抓包,输入错误密码,检查得到是使用 ws 通讯的。包结构如下:
这是登录时产生的五个数据包,通过这几个包,我们可以看到最前面有几(4)个字节是未知的,然后后面跟着一串JSON(字符串)数据。既然是字符串和二进制数据混合,那么可以假设前面的二进制包含一个字符串长度的字段。以握手包为例,(注意,这里 0100 0064 是 二进制数据的 16进制表示,而后面的JSON字符串由于已知,就直接显示原字符串了,一下类同,空格是为了方便查看添加的)0100 0064 {"sys":{"type":"js-websocket","version":"0.0.1","rsa":{},"protoVersion":"2zWPnSlRsRxjqgcU215Uxg=="}}数了一下,除了开头4个字节(0100 0064)外,后面的JSON字符串长度为100字符,换算16进制为 0x64,正好对应了第4个字节64。虽然不确定,但是暂时可以猜测应该是这个(后面的其他数据包可以印证)好,继续看其他的数据包有什么规律没。握手返回包 JSON字符串36个字符,0x24。0100 0024 {"code":200,"sys":{"useProto":true}}空白包,疑似确认0200 0000登录包 假如 0x66 是数据包长度,那么这里 JSON 也应该是 102 个字符,可是这里的 JSON 只有 72 个字符。
继续往前加,签名那串字符串也才 27 个字符,加起来才 99 个字节,还差三个,但是加上 0001 1b 就正好了,所以 0001 1b 可能也属于后面的内容。0400 0066 0001 1b gate.gateHandler.queryEntry{"login_email":"Test1","login_pwd":"123451","code":"fdfdd","is_r":false}自此,可以猜测前面4个字节中至少有一个字节是表示后面内容长度的,但是考虑内容长度很容易就超过255字节,所以这个长度应该不止一个字节,但是是往前拓展还是往后拓展,暂时不确定。由于数据包太少不够参考,我们登陆成功看下后续的数据包。登陆成功时的数据包0400 0125 0401 {"code":200,"host":"127.0.0.1","port":13053,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IlRlc3QxIiwibWFwIjo5LCJpZCI6IjVmMDg3MTE0MGRmNDJmMDZmYTQ3MTBiYSIsIm5pY2tuYW1lIjoiVGVzdDEiLCJpYXQiOjE1OTQ2MTk4NjAsImV4cCI6MTU5NDYxOTkyMH0.09FxTtK2i7WOgYnPGeLFIPL6xrMZTxFmAUwUHNfHiII","mid":9}登陆成功后,会断开 ws 链接重新建立一个新端口的链接,所以应该是登陆服务器和游戏服务器在不同的服务器/端口上。新的链接依然会有一个握手包和上面的一样,但握手返回的包不一样。握手返回包0100 2339 {"code":200,"sys":{"heartbeat":3,"dict":{"chat.chatHandler.send":1,"connector.entryHandler.enter":2,...握手确认包0200 0000登录包0400 0104 0101 0011 {"email":"Test1","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IlRlc3QxIiwibWFwIjo5LCJpZCI6IjVmMDg3MTE0MGRmNDJmMDZmYTQ3MTBiYSIsIm5pY2tuYW1lIjoiVGVzdDEiLCJpYXQiOjE1OTQ2MTk4NjAsImV4cCI6MTU5NDYxOTkyMH0.09FxTtK2i7WOgYnPGeLFIPL6xrMZTxFmAUwUHNfHiII"}收到空白包0300 0000收到推送包0400 2f30 0401 {"code":200,"msg":"success","data":{"userTasks":...由于返回的内容比较长,就没全部显示,握手返回包与收到推送包是一个纯 JSON 字符串,长度为 9017(0x2339) 字符,不用猜了,第3-4字节一定是内容长度。而且怀疑 0100 中 01 应该是包的类型,或者seq,因为发 0100, 收 0100,接着发 0200 0000 但没有返回。但是接下来发的包就不是 0300 了,而是 0400,而且后续的几乎所有包,都是 0300 或 0400 开头,所以猜测为数据包类型。通过查看后续大量的数据包,总结规律,0300 0000 应该是心跳包,固定收发相同内容。然后有内容的数据包都是 0400 开头,既然如此,我们暂时可以推测数据包格式如下。{Type}00 {BodyLength} {Body}- Type 一个字节 01=握手 02=握手确认(典型TCP三次确认) 03=心跳 04=数据包
- BodyLength 2个字节 表示 Body 的内容长度
- Body 正文
而 {Body} 中有时候前面还会有一段未知数据段 0401、0101 0011、0001 1b gate.gateHandler.queryEntry这三个个还不一样,但是有一个有字符串,依然猜测需要有地方来表示字符串长度,而 gate.gateHandler.queryEntry 长度为 27 (0x1b),那就不用猜了,就是这个位置了,而这个字符串应该就是概念意义上的路由了。之前返回了字典,这个字符串也出现在了字典里,合理怀疑字典就是路由的map,用来减少数据包长度的。但位置未知。继续分析后面的大量数据包,发现前面第二位在逐渐递增,合理怀疑为SEQ,这里讲解一下为什么会有SEQ这个东西,0400 003d 0607 onLeave{"route":"onLeave","uid":"5f02ad4dd6827e0184c1316c"}
0400 00b6 0609 onChatMsg{"msg":"恭喜 <span style='color:red;'>lll </span> 捕捉<span style='color:red;'> <span style='color:purple;'>粉红海兔</span></span> -> 成功","type":1,"channel":0}
0400 000d 0102 0024 {"mid":9}
0401 614e 0402 {"data":{"...在我们普通 http 中,可能没这个概念,因为我们的 http 请求某种意义上来说是阻塞的,就是你发送一个请求以后会一直等待这个链接返回数据,在返回之前,你无法继续用这个链接发送其他数据。所以我们不会有数据包弄混的情况。
但是在 TCP 或者 ws 中,数据都是异步的,所以如果同时向服务器发送多个数据包,然后服务器又返回好几个,怎么和原来的请求对应上呢?所以这里就有一个 SEQ 的概念,全称叫做“Sequence Number”,感兴趣的可以查看一下相关资料,这也是 TCP 中,或者异步通讯中经常出现的一个概念。具体我也只是知道,不能说的太多。然后 Body 的第一个字节,总是有一定的规律,和后面的格式有一定关联,所以猜测应该是标记后面的格式或者类型的。至于 Type = 01 时 后面两个字节,猜测和 Dict 有关系,和几个事件对了一下,和 Dict 中描述的是一致的,所以应该是压缩字典。所以目前猜出的 Body 包中格式大概如下:{Type} {SeqId} {TypeArgs} {Data}其中格式如下- Type 1字节 对应 TypeArgs 的结构
- 00 发送 {TypeLength} {TypeString}
- 01 发送 {DictID}
- 04 返回 无 TypeArgs
- 06 返回 {TypeLength} {TypeString}
- SeqId 1字节
- Data 数据,为 JSON 字符串。
至此,分析结果已经明了。这次分析也看到,一些框架为了减少数据流大小,对Route也是尽可能压缩,通过字典来压缩。
|
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|