游戏产业和互联网都是互通的,随着 Http2.0-未来版本,以及更多行业也有更多非 Http 请求的工作任务。
协议已经开始需要掌握:
http 镞,websocket ,基础 Tcp,基础 Udp
Rpc,webRtc,Tars,Kcp,ENet
序列化数据结构
Protobuf,Thrift,Json,xml
网络编程能力已经慢慢成为测试人员的一项必备能力了。
拿游戏产业来说可以有以下作用:
1.接口测试
2.压力测试
3.mock-造环境数据(需要附加 ssh 服务器,导入所有配置表,修改数据库)
4.mock-GM 工具(需要附加 Mock 和做一些类似数据库存储过程的功能)
5.平衡性测试(新手引导职业升级速度,抽卡,Pve 副本,Pvp 胜率,活动完成等)
6.战斗验证(帧同步,状态同步)前后端在开发语言有不少小细节。
7.日志文件同步
然而这块学习是有一定难度的,一部分原因是因为不能和自动化(随时找个网页和 app)和 http 接口(有教学接口),有练手场景。后面练习,需要自己写服务端才能练习。
还有一部分是需要网络知识,希望通过这个补全和掌握这块的知识空缺。
Client to Server 客户端担任 send 发送 消息--->服务器
Server to Client 客户端担任 recv 接收 <----消息<----服务器
Server push Client 服务器主动推送给客户端的消息,比如小红点,比如一些夹带触发的消息,比如打怪升级了,服务器会 push 升级消息给 Client.
这个里面细节是很多的。比如 C 和 S 两端链接是走了 message queue,服务器 mq 按帧去保存,比如最多保存 500 帧,每 500 帧清除一次。比如中间有压缩 ,签名或者加密,顺序不能乱,比如内存对齐和补位。
这个里面发送的消息,消息是一个对象,对象有数据结构,这块是网络编程时最先需要学习。
在建立 socket 之后,socket 设置数据类型 ,必须设置基础的,Ipv4 还是 ipv6,数据流 (tcp) 还是数据包 (udp)
在建立链接也就是管道后,返回数据也有一些细节。
建立管道成功后,也就是开始发包,这个后面详细讲。
Client-->(msg 原始数据容器 ) 缓存区-->(二进制) 网卡-->(二进制) 网关或者其他-->(二进制) 服务器
这里注意,一定要区分内存里数据长得样子(给机器使用得)和自己看得(排错用得,基本都是用 16 进制)
自己看得就不能当成 msg 原始形态,比如 C# toString("x2") 就是转 16 进制,但是发得时候你转 16 进制发就不正确了。
为什么不能那么转,需要了解下不同字符的长度,有些字符是会在二进制转换环节被自动翻译去除字符的 比如 "\x"。
msg 原始数据容器 分为以下几种类型:
容器在这里就是内存里面装 msg 的,容器不是基础数据类型
1.字符串 字符串没啥好说的,Python 不区分 char 和 string,在使用时非二进制的,这个在转到网卡那边时还是会翻译成二进制的。
2.字节 输出是 b 开头的,说明是 bytes,多个 byte 组成的,也是字节数组,字节也可以拼接成长的字节。字符串转字节是通过 encode() 转换。
比如 "hello world".encode() -->b'hello world'。这里做得就是通过编译器对字符串进行编码,进行编码时默认为 utf-8。utf-8 向下兼容 ascii 码。
3.bytearray bytes 都是一样,但是 bytearray 内存可变的。
内存可变和不可变怎么来区分呢,因为上面三种数据类型一定是迭代器,迭代器就是可以被遍历的。
t1 = "hello world"
t1[0] = "a"
t2 = "hello world".encode()
t2[0] = "a"
#上面这么写都是会抛错的,因为内存不可变
bytearray 可以使用 对象 [0] 去赋值。
t2 = bytearray([1,2,3])
t2[0] =4
print(t2) # \x04 就是因为t2[0]修改了,bytearray(b'\x04\x02\x03')
可变和不可变在网络编程里面也是有关系的,不可变的可以用于形参,可变的不推荐用形参会,调用函数会多拷贝一个副本。
上面三种基础的数据类型,bytes 和 bytearray 在原始形态下是二进制,大部分情况下,只需要使用 bytes 就能当网络编程的容器了。
这里要做到 客户端把原始数据进行打包投递到网关。
这里有个概念是 服务器并不是客户端发什么都接收的,所有客户端需要知道服务器能接收什么数据类型(就是二进制对服务器来说会转换成什么)。
这里先不定义一个 socket 来回发,通过都是先要知道自己发过去的是对的。
下面选择用最常见的 bytes,题目为客户端需要发送 bytes 数据,每次不能超过 1450 个字节(最大传输单元),发送结果打印到控制台。
这里有个重要知识点,bytes 不是字节,是字节数组,一个 byte 占一个字节,基础数据类型 (部分引用数据类型) 在缓存区里面占不同的位数,所以可以理解为 bytes 的长度也就是这个字节数组的长度。
def session_bytes(datas: bytes,mtu:int=1450):
"""
客户端需要发送bytes数据,网络字节序是大端
每次最大传输单元1450个字节,超过就分成2段传
byteorder 代表主机字节序,big是大端。
.to_bytes(2,byteorder="big")的出现是因为字节数组是二进制的,所以在遍历时在内存通过ord()转换成了int。
:return:
"""
msg =[b"",b""]
if len(datas) >= mtu:
for i in datas[:mtu]:
msg[0] += i.to_bytes(2,byteorder="big")
for i in datas[mtu:]:
msg[1] += i.to_bytes(2,byteorder="big")
else:
for i in datas:
msg[0] += i.to_bytes(2,byteorder="big")
return msg
Python 语法不解释,注意看注释。msg 是有序,网络传输在应用层都是有序的,在网络层自己重传和组合是网络层操作的,应用层 Python 是用不到的。
字节序决定了最高位字节在字节数组的开头还是结束,是指具体内容先传入的在左边还是在右边,左边就是开头,这种是 big 大端。
客户端本地显示是不重要的,默认本机的可以使用 print(sys.byteorder) 打印,上面例子设置字节序 big 为大端,是标记网络缓存区 - 网卡那层传输用的。字节序只要和目标端服务端一致就行。
Tips:就是把 mtu 传入时 bytes 数据类型数据给改短用于测试。
Json 这里就不用多讲了,Json 和内存打交道和文件打交道是分开的。内存打交道 json.loads() 和 json.dumps()
def json_to_bytes(json_data: dict) -> bytes:
"""
Json转换字节数组
:param json_data: {"cat": "maomao"}
:return: bytes
"""
if isinstance(json_data, (str,dict)):
return json.dumps(json_data).encode("utf-8")
上面例子 Json 转成 bytes。
import xmltodict #pip install xmltodict
def xml_to_bytes(xml_out_put: str)-> bytes:
"""
xml转换字节数组
:param xml_out_put:
:return:
"""
xml_data = xmltodict.parse(xml_out_put)
data_json = json.dumps(xml_data,ensure_ascii=False)
data_dict = json.loads(data_json)
if isinstance(data_dict, (str, dict)):
return json.dumps(data_dict).encode("utf-8")
xml 这种比较远古了,但是优点是带做个平级区域和坐标的概念。
以上内容基本上面都有,练习到完全不用查网上就能写。
1.字符串和 bytes 和 bytearray 的练习,操作切片,同类型拼接,不满足类型转换,长度。
2.学习大端和遍历 bytes 时的转换。
3.Json 和内存打交道二个方法,xml 部分可以顺带理解下。