9个版本
0.2.0 | 2022年4月18日 |
---|---|
0.1.7 | 2022年4月18日 |
0.1.3 | 2022年3月29日 |
#2115 in 编码
81 每月下载量
36KB
615 行
WSBPS-rust
我的Rust WebSocket二进制报文实现。这个库只实现了报文的序列化和反序列化,让您可以使用您喜欢的任何WebSocket系统。
指南
指南用于包宏中,用于定义每个对象应实现哪种类型的特质。这可以减少在不需要时实现的特质的数量。
指南表示为箭头。以下是对每个箭头的含义
箭头在代码中表示为 <-, ->, 和 <-> 美观的箭头仅在文档中使用
← 左箭头
<-
左箭头用于只读数据,这将只为此项实现可读特质。
→ 右箭头
->
右箭头用于只写数据,这将只为此项实现可写特质。
↔ 双头箭头
<->
双头箭头用于可读可写的数据,这实现了可读和可写特质。
数据类型
以下是可以通过此报文系统传输的数据类型表,以及它们在其它语言中的对应类型和一些自定义数据类型。
标准数字类型
Rust类型 | 范围 | Javascript (wsbps-js) | 长度(字节) |
---|---|---|---|
i8 | -128到127 | number (i8) | 1 |
i16 | -32768到32767 | number (i16) | 2 |
i32 | -2147483648到2147483647 | number (i32) | 4 |
u8 | 0到255 | number (u8) | 1 |
u16 | 0到65535 | number (u16) | 2 |
u32 | 0到4294967295 | number (u32) | 4 |
f32 | -3.4e+38到3.4e+38 | number (f32) | 4 |
f64 | -1.7e+308到+1.7e+308 | number (f64) | 4 |
表中列出的所有数字类型都使用大端序进行编码
可变长度数字
VarInt
VarInts是一种可以大小从0到4294967295的范围的数字,可以作为从1字节到4字节长度的二进制数据发送
VarInts / VarLongs 以每 7 位一次进行序列化,从最低有效位(lsb)开始,每个输出字节的最高有效位(msb)表示是否存在续传字节(msb = 1)。
示例
VarInt | 二进制 | 字节格式 |
---|---|---|
1 | 00000001 | 1 |
127 | 01111111 | 127 |
128 | 10000000 00000001 | 128, 1 |
255 | 11111111 00000001 | 255, 1 |
300 | 10101100 00000010 | 172, 2 |
16384 | 10000000 10000000 00000001 | 128, 128, 1 |
2097152 | 10000000 10000000 10000000 00000001 | 128, 128, 128, 1 |
如您所见,这种格式在存储可变长度的数据方面效率更高,但是 VarInt 的最大长度与 u32(无符号 32 位整数)相同。
VarLong
VarInt 数据类型只能向上移动 5 个偏移量,这限制了它只能处理 u32 数字。另一方面,VarLong 可以向上移动多达 10 个偏移量,这意味着它可以编码和处理从 0 到 18446744073709551615(u64)的所有数字。
VarLongs 的编码方式与 VarInts 相同,只是当读取时允许更多的移位,这些移位被分开以减少 VarInts 的内存分配,因此除非必要,否则不需要将它们分配为 u64(VarLong)。
布尔值
布尔值编码为一个字节,1 表示真值,0 表示假值。
字符串
字符串使用 VarInt 编码字符串长度,后面跟着 UTF-8 编码的字节序列,长度等于 VarInt 长度减 1。
Length VarInt
Contents [u8; Length]
数组
数组数据类型使用向量,这些向量以与字符串相同的方式进行编码,首先是数组长度的 VarInt,然后是该数组所有相应值的编码序列,紧跟在 VarInt 之后。
Length VarInt
For Length {
Item Any
}
您可以使用 Vec<Type>
来表示这些类型,这是此实现的常见方式,它被表示为 Vec<u8>
。
包组
要创建包,您使用包宏。在宏内部,您必须指定包 "组"。这些组用于处理客户端和服务器包 ID 之间的差异。定义组的语法如下
use wsbps::*;
packets! {
GroupName (Direction) {
//... Packets
}
}
在这个例子中,您将用包组的名称替换 "GroupName",并用表示此包组方向的箭头替换 "Direction"。
此宏将生成一个具有提供的组名的枚举,如果实现了读取方向,则可以用来读取包。
包
然后,您可以按照以下结构定义包。应在组块内部使用此结构。
Name (ID) {
example: u8
// Normal struct field:type
}
在这个例子中,您应该用包的名称替换 "Name"。您提供的名称也是生成的结构体的名称。"ID" 应替换为该包的唯一标识符。ID 使用 VarInt 编码,因此它们可以从 0x00000000 - 0xffffffff(0 - 4294967295)。
以下是将包组和包实现结合在一起的示例。
use wsbps::*;
packets! {
BiPackets (<->) {
APacket (0x01) {
user: u8
}
}
ServerPackets (->) {
BPacket (0x00) {
name: u8
}
}
ClientPackets (<-) {
CPacket (0x00) {
test: u8,
test2: u8
}
}
}
结构体 & 枚举
如果您想在包中使用自定义的结构体或枚举,有两种选择。
选项 1
packet_data 宏
您可以使用 packet_data 宏轻松创建用于包中的枚举和结构体。此宏将自动为提供的枚举/结构体生成所需的读取和写入特性。
use wsbps::*;
packet_data! {
enum Test (<->) (VarInt) {
X: 1,
B: 999
}
struct TestStruct (->) {
Name: String
}
}
第一组括号包含枚举/结构体类型的 "Direction",它告诉它需要实现哪些特性。枚举中的第二组括号包含枚举的数据类型,在这种情况下,使用 VarInt 数据类型。任何整数数据类型都是可接受的。
选项 2
如果您的数据需要自定义编码,或者过于复杂,无法在结构体或枚举中描述,您可以手动实现io模块中的Readable和Writable特质。
impl Writable for SomeType {
fn write<B: Write>(&mut self, o: &mut B) -> Result<()> {
// Your writing logic
Ok(())
}
}
impl Readable for SomeType {
fn read<B: Read>(i: &mut B) -> Result<Self> where Self: Sized {
// Your reading logic
}
}
依赖项
~0.4–0.9MB
~20K SLoC