Rust通用TCP/UDP服务端客户端:枚举与字节的序列化反序列化实现
Rust TCP/UDP通用消息系统:枚举序列化/反序列化实现方案
这个思路完全可行,是网络服务/游戏场景中常用的消息建模方式,以下是两种落地方案:
一、用成熟序列化库快速实现(推荐)
基于serde(Rust生态最通用的序列化框架)搭配二进制序列化库borsh(专为高性能二进制场景设计,对枚举支持友好)实现,无需手动处理变体ID和字节转换。
步骤1:添加依赖
在Cargo.toml中加入:
[dependencies] serde = { version = "1.0", features = ["derive"] } borsh = "1.0"
步骤2:给枚举添加序列化/反序列化派生宏
修改枚举定义:
use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug, Clone)] enum Messages { Welcome, Position(f32, f32), Damages(u8), }
步骤3:实现泛型send和message_received函数
use borsh::{BorshSerialize, BorshDeserialize}; // 序列化消息并返回字节流+变体ID fn send<E: BorshSerialize + Serialize>(message: E) -> Result<(Vec<u8>, usize), borsh::Error> { let data = message.try_to_vec()?; // 从序列化逻辑中提取变体ID(基于枚举定义顺序) let variant_id = get_variant_id(&message); Ok((data, variant_id)) } // 从字节流反序列化消息 fn message_received<E: BorshDeserialize>(data: Vec<u8>) -> Result<E, borsh::Error> { E::try_from_slice(&data) } // 辅助函数:提取枚举变体ID fn get_variant_id<E: Serialize>(message: &E) -> usize { use serde::ser::{Serializer, SerializeStructVariant}; struct IdSerializer; impl Serializer for IdSerializer { type Ok = usize; type Error = serde::ser::Error; type SerializeSeq = serde::ser::Impossible<usize, Self::Error>; type SerializeTuple = serde::ser::Impossible<usize, Self::Error>; type SerializeTupleStruct = serde::ser::Impossible<usize, Self::Error>; type SerializeTupleVariant = serde::ser::Impossible<usize, Self::Error>; type SerializeMap = serde::ser::Impossible<usize, Self::Error>; type SerializeStruct = serde::ser::Impossible<usize, Self::Error>; type SerializeStructVariant = IdStructSerializer; fn serialize_struct_variant( self, _: &'static str, index: usize, _: &'static [&'static str], _: impl serde::ser::SerializeStructVariantVisitor, ) -> Result<Self::Ok, Self::Error> { Ok(index) } // 其他方法默认返回错误(仅处理结构体变体) fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_str(self, _: &str) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_none(self) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_some<T>(self, _: &T) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_unit_variant(self, _: &'static str, _: usize, _: &'static str) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_newtype_struct<T>(self, _: &'static str, _: &T) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize { Err(serde::ser::Error::custom("not a struct variant")) } fn serialize_newtype_variant<T>(self, _: &'static str, _: usize, _: &'static str, _: &T) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize { Err(serde::ser::Error::custom("not a struct variant")) } } struct IdStructSerializer; impl serde::ser::SerializeStructVariant for IdStructSerializer { type Ok = usize; type Error = serde::ser::Error; fn end(self) -> Result<Self::Ok, Self::Error> { Err(serde::ser::Error::custom("not implemented")) } fn serialize_field<T>(&mut self, _: &'static str, _: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize { Ok(()) } } message.serialize(IdSerializer).unwrap() }
注:若不需要手动获取变体ID,
send函数可简化为直接返回序列化后的字节流——borsh已将变体ID编码在字节流头部。
二、手动实现序列化/反序列化(完全掌控逻辑)
如果需要自定义序列化规则,可手动处理变体ID和字节转换:
步骤1:定义消息Trait与枚举
// 通用消息Trait trait NetworkMessage: Sized { fn serialize(&self) -> Vec<u8>; fn deserialize(data: &[u8]) -> Result<Self, &'static str>; fn variant_id(&self) -> usize; } // 消息枚举实现Trait #[derive(Debug, Clone)] enum Messages { Welcome, Position(f32, f32), Damages(u8), } impl NetworkMessage for Messages { // 序列化:写入变体ID+字段字节 fn serialize(&self) -> Vec<u8> { let mut buf = Vec::new(); match self { Messages::Welcome => buf.push(0x00), Messages::Position(x, y) => { buf.push(0x01); // 用大端字节序保证跨平台一致 buf.extend_from_slice(&x.to_be_bytes()); buf.extend_from_slice(&y.to_be_bytes()); } Messages::Damages(dmg) => { buf.push(0x02); buf.push(*dmg); } } buf } // 反序列化:读取ID后匹配变体 fn deserialize(data: &[u8]) -> Result<Self, &'static str> { if data.is_empty() { return Err("empty data"); } let id = data[0]; let rest = &data[1..]; match id { 0x00 => Ok(Messages::Welcome), 0x01 => { if rest.len() != 8 { return Err("invalid position data length"); } let x = f32::from_be_bytes(rest[0..4].try_into().unwrap()); let y = f32::from_be_bytes(rest[4..8].try_into().unwrap()); Ok(Messages::Position(x, y)) } 0x02 => { if rest.len() != 1 { return Err("invalid damages data length"); } Ok(Messages::Damages(rest[0])) } _ => Err("unknown variant id"), } } // 返回变体ID fn variant_id(&self) -> usize { match self { Messages::Welcome => 0x00 as usize, Messages::Position(_, _) => 0x01 as usize, Messages::Damages(_) => 0x02 as usize, } } }
步骤2:实现泛型发送/接收函数
fn send<E: NetworkMessage>(message: E) -> (Vec<u8>, usize) { (message.serialize(), message.variant_id()) } fn message_received<E: NetworkMessage>(data: Vec<u8>) -> Result<E, &'static str> { E::deserialize(&data) }
关键注意事项
- 服务端与客户端必须使用完全一致的枚举定义(包括变体顺序、字段类型、ID映射),否则会出现序列化/反序列化错误。
- 手动实现时务必保证字节序一致(推荐用大端字节序),避免跨平台兼容性问题。
- 变体数量较多时,手动实现容易出错,优先选择成熟序列化库。
内容的提问来源于stack exchange,提问作者LucioleMaléfique
相关产品推荐
相关产品推荐

