57个版本 (26个稳定版)
3.4.0 | 2022年9月10日 |
---|---|
3.2.0 | 2021年12月1日 |
3.1.9 | 2021年2月13日 |
3.1.7 | 2020年7月15日 |
0.3.4 | 2018年2月21日 |
#82 在 #tcp-udp
977 每月下载量
在 3 crate 中使用
35KB
693 行
协议
Rust中的轻松协议定义。
此crate添加了一个自定义derive,可以添加到类型中,允许结构化数据在任何IO流中发送和接收。
网络内置,特别支持TCP和UDP。
您定义的协议也可以用于网络之外 - 请参阅 Parcel::from_raw_bytes
和 Parcel::raw_bytes
方法。
此crate还提供
请查看 示例 文件夹以了解使用方法。
用法
将此添加到您的 Cargo.toml
[dependencies]
protocol = { version = "3.4", features = ["derive"] }
然后使用 #[derive(protocol::Protocol)]
属性定义一个类型
#[derive(protocol::Protocol)]
struct Hello {
pub a: String,
pub b: u32,
}
内部原理
这里最有趣的部分是 protocol::Parcel
特性。任何实现了此特性的类型都可以序列化为字节流,并从字节流反序列化。所有原始类型、标准集合、元组和数组都实现了此特性。
当您定义自己的 Parcel
类型时,这个包特别有用。您可以使用 #[derive(protocol::Protocol)]
来实现这一点。请注意,为了使类型实现 Parcel
,它还必须实现 Clone
、Debug
和 PartialEq
。
#[derive(Parcel, Clone, Debug, PartialEq)]
pub struct Health(f32);
#[derive(Parcel, Clone, Debug, PartialEq)]
pub struct SetPlayerPosition {
pub position: (f32, f32),
pub health: Health,
pub values: Vec<String>,
}
自定义推导
任何用户定义的类型都可以自动推导出 Parcel
特性。
示例
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
pub struct Handshake;
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
pub struct Hello {
id: i64,
data: Vec<u8>,
}
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
pub struct Goodbye {
id: i64,
reason: String,
}
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
pub struct Node {
name: String,
enabled: bool
}
#[protocol(discriminant = "integer")]
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
pub enum PacketKind {
#[protocol(discriminator(0x00))]
Handshake(Handshake),
#[protocol(discriminator(0xaa))]
Hello(Hello),
#[protocol(discriminator(0xaf))]
Goodbye(Goodbye),
}
fn main() {
use std::net::TcpStream;
let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
let mut connection = protocol::wire::stream::Connection::new(stream, protocol::wire::middleware::pipeline::default());
connection.send_packet(&Packet::Handshake(Handshake)).unwrap();
connection.send_packet(&Packet::Hello(Hello { id: 0, data: vec![ 55 ]})).unwrap();
connection.send_packet(&Packet::Goodbye(Goodbye { id: 0, reason: "leaving".to_string() })).unwrap();
loop {
if let Some(response) = connection.receive_packet().unwrap() {
println!("{:?}", response);
break;
}
}
}
枚举
区分符
枚举值可以通过其基于1的变体索引或通过发送每个变体的字符串名称进行传输。
注意:默认行为是使用 作为字符串的变体名称(string
)。
可以通过 #[protocol(discriminant = "<type>")]
属性来更改此行为。
支持的区分符类型
string
(默认)- 这会将枚举变体名称作为网络传输的区分符
- 这会使用更多的字节,但非常灵活
整数
- 这会将基于1的变体编号作为网络传输的区分符
- 如果枚举变体具有显式的区分符,则
- 枚举变体不能在源代码中重新排序,否则会破坏协议
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
#[protocol(discriminant = "string")]
pub enum PlayerState {
Stationary,
Flying { velocity: (f32,f32,f32) },
// Discriminators can be explicitly specified.
#[protocol(discriminator("ArbitraryOverTheWireName"))]
Jumping { height: f32 },
}
杂项
您可以在序列化时重命名变体。
#[derive(protocol::Protocol, Clone, Debug, PartialEq)]
#[protocol(discriminant = "string")]
pub enum Foo {
Bar,
#[protocol(name = "Biz")] // the Bing variant will be send/received as 'Biz'.
Bing,
Baz,
}
依赖关系
~1.5MB
~35K SLoC