rust 枚举到字节/字节到枚举?

ff29svar  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(159)

我试图建立一个通用的Rust服务器/客户端(tcp和udp)系统。
最初的想法是允许用户创建一个枚举作为消息包,但这需要一种方法来将它们序列化/反序列化为字节,而我在网上找不到任何关于它的东西。让我们考虑一下:

enum Messages {
    Welcome,
    Position(f32, f32),
    Damages(u8),
}

首先,在序列化部分,我找不到如何知道每个变量的大小,以及如何记录所有变量。

fn send<E>(message: E) {
    // create a Vec<u8> from the enum message, and an id (enum variant id as usize ?)
}

而在另一个大小上,我甚至不知道如何重建这样一个枚举:

fn message_received<E>(data: Vec<u8>, id: usize) -> E {
    // get the E enum variant from the id
    // write all the enum fields with the data
}

当然,其思想是服务器端和客户端的枚举是相同的。
看到我不知道如何做这一切,我想知道这整个想法是不是不可行?但是,如何实现这样一个通用的消息系统呢?

yshpjwxd

yshpjwxd1#

我建议使用serde(串行化-反串行化)机箱。

use serde::{Deserialize, Serialize};
use serde_json;

# [derive(Debug, Serialize, Deserialize)]

enum Messages {
    Welcome,
    Position(f32, f32),
    Damages(u8),
}

fn serialize(message: Messages) -> Vec<u8> {
    serde_json::to_vec(&message).unwrap()
}

fn deserialize(bytes: &[u8]) -> Messages {
    serde_json::from_slice(bytes).unwrap()
}

fn main() {
    // on the server
    let message: Messages = Messages::Position(1.2, 3.4);
    let bytes: Vec<u8> = serialize(message);
    println!("{bytes:?}"); // [123, 34, 80, 111, ...]

    // on the client
    let message_deserialized: Messages = deserialize(&bytes);
    println!("{message_deserialized:?}"); // Position(1.2, 3.4)
}

正如你所看到的,这个方法使用JSON作为中介来存储你的枚举数据,在一些应用程序中,这可能太慢了(u64在你的代码中是8个字节,但是一旦转换成JSON,每个字符需要1个字节,基数为10,增加了很多消息的总大小,也增加了传输时间),在这种情况下,创建自定义函数可能是必要的。


# [derive(Debug)]

enum Messages {
    Welcome,
    Position(f32, f32),
    Damages(u8),
}

fn serialize(message: Messages) -> Vec<u8> {
    match message {
        Messages::Welcome => vec![0],
        Messages::Position(x, y) => {
            let mut buffer = vec![1];
            buffer.extend_from_slice(&x.to_le_bytes());
            buffer.extend_from_slice(&y.to_le_bytes());
            buffer
        }
        Messages::Damages(damages) => {
            vec![2, damages]
        }
    }
}

fn deserialize(bytes: &[u8]) -> Messages {
    match bytes[0] {
        0 => Messages::Welcome,
        1 => {
            let x = f32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
            let y = f32::from_le_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]);
            Messages::Position(x, y)
        }
        2 => {
            Messages::Damages(bytes[1])
        }
        _ => panic!("Unknown message"),
    }
}

fn main() {
    // on the server
    let message: Messages = Messages::Position(1.2, 3.4);
    let bytes: Vec<u8> = serialize(message);
    println!("{bytes:?}"); // [1, 154, 153, 153, 63, 154, 153, 89, 64] (way smaller !)

    // on the client
    let message_deserialized: Messages = deserialize(&bytes);
    println!("{message_deserialized:?}"); // Position(1.2, 3.4)
}

上面的例子是一种手动实现这个系统的可能方法。但是如果时间不受严格限制的话,我建议使用第一种方法,因为第二种方法编写起来更耗时,而且更容易出错。
使用枚举是否可行,取决于您要实现的目标。

相关问题