70 个版本 (25 个稳定版本)

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.1.10 2016年9月18日

#250 in 网络编程

Download history 27/week @ 2024-03-13 19/week @ 2024-03-20 31/week @ 2024-03-27 61/week @ 2024-04-03 13/week @ 2024-04-10 14/week @ 2024-04-17 82/week @ 2024-04-24 209/week @ 2024-05-01 408/week @ 2024-05-08 391/week @ 2024-05-15 306/week @ 2024-05-22 281/week @ 2024-05-29 246/week @ 2024-06-05 225/week @ 2024-06-12 230/week @ 2024-06-19 176/week @ 2024-06-26

916 每月下载量
用于 4 个包

MIT 许可证

105KB
2K SLoC

协议

Build Status Crates.io MIT licensed

文档

Rust 中轻松的协议定义。

此包添加了一个自定义 derive,可以添加到类型中,允许结构化数据从任何 IO 流发送和接收。

网络是内置的,并特别支持 TCP 和 UDP。

您定义的协议也可以在网络之外使用 - 请参阅 Parcel::from_raw_bytesParcel::raw_bytes 方法。

此包还提供

  • TCPUDP 模块,用于轻松发送和接收 Parcel
  • 一个通用的 中间件 库,用于自动转换发送/接收的数据
    • 已经编写了支持 压缩 的中间件
    • 可以通过一个具有两个方法的 trait 实现自定义中间件

查看 示例文件夹 了解用法。

用法

将此添加到您的 Cargo.toml

[dependencies]
protocol = { version = "3.4", features = ["derive"] }

然后使用具有以下属性的 #[derive(Protocol)] 特性定义一个类型

#[derive(protocol::Protocol)]
struct Hello {
    pub a: String,
    pub b: u32,
}

底层

这里最有趣的部分是 protocol::Parcel 特性。任何实现了这个特性的类型都可以被序列化为字节流,并从字节流反序列化。所有原始类型、标准集合、元组和数组都实现了这个特性。

当您定义自己的 Parcel 类型时,这个包特别有用。您可以使用 #[derive(Protocol)] 来实现。请注意,为了使一个类型实现 Parcel,它还必须实现 CloneDebugPartialEq

#[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 特性。

示例

#[macro_use] extern crate protocol_derive;
#[macro_use] extern crate protocol;

#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Handshake;

#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Hello {
    id: i64,
    data: Vec<u8>,
}

#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Goodbye {
    id: i64,
    reason: String,
}

#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Node {
    name: String,
    enabled: bool
}

#[protocol(discriminant = "integer")]
#[derive(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, 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, Clone, Debug, PartialEq)]
#[protocol(discriminant = "string")]
pub enum Foo {
  Bar,
  #[protocol(name = "Biz")] // the Bing variant will be send/received as 'Biz'.
  Bing,
  Baz,
}

依赖关系

~3–4.5MB
~88K SLoC