跳转到内容

TLV 通信格式实现

https://en.wikipedia.org/wiki/Type-length-value

代码仓库:https://github.com/zooeywm/playground/blob/main/rs/tlv/tests/tlv.rs

使用 binrw 库,可以轻松实现 tlv,包括自定义协议细节

以下是示例代码:

//! TLV encoded scheme information
//! For checking the use of messaging target, we divide Message into three types: BiDirect,
//! FromRemote, and ToRemote.
//!
//! So we can call read and write on `[TestMessage]`
//! Only can call read on `[FromTestMessage]`
//! Only can call write on `[ToTestMessage]`
//!
//! **WARN**: The fields are read or write by sort, so change them carefully.
//!
//! **NOTE**: For unknown reason, when add cfg-test derive `BinWrite` to `[FromTestMessage]`, the
//! rust-analyzer will report an error, but can build, run, or test successfully. But do the
//! similar thing to `[ToTestMessage]` will not result in an rust-analyzer error. I've test it,
//! but to avoid ra reporting error, I commented the write test codes for `[FromTestMessage]`.
use binrw::{BinRead, BinResult, BinWrite};
use serde::{Deserialize, Serialize};
#[derive(BinRead, BinWrite)]
#[brw(little)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub struct TestMessage {
pub r#type: TestMessageType,
#[bw(write_with = custom_writer)]
#[br(parse_with = custom_reader)]
pub value: TestMessageValue,
}
#[derive(BinRead)]
#[br(little)]
// #[cfg_attr(test, derive(BinWrite, Debug, Eq, PartialEq), bw(little))]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub struct FromTestMessage {
pub r#type: FromTestMessageType,
#[br(parse_with = custom_reader)]
// #[cfg_attr(test, bw(write_with = custom_writer))]
pub value: TestMessageValue,
}
#[derive(BinWrite)]
#[bw(little)]
#[cfg_attr(test, derive(BinRead, Debug, Eq, PartialEq), br(little))]
pub struct ToTestMessage {
pub r#type: ToTestMessageType,
#[bw(write_with = custom_writer)]
#[cfg_attr(test, br(parse_with = custom_reader))]
pub value: TestMessageValue,
}
#[derive(BinRead, BinWrite)]
#[brw(little, repr = u32)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub enum TestMessageType {
HeartBeat = 0x1,
}
#[derive(BinRead)]
#[br(little, repr = u32)]
// #[cfg_attr(test, derive(BinWrite, Debug, Eq, PartialEq), bw(little, repr = u32))]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub enum FromTestMessageType {
Message1 = 5,
Message2,
Message3,
Message4,
Message5,
Message6,
Message7,
Message8,
Message9 = 17,
}
#[derive(BinWrite, BinRead, Debug, Eq, PartialEq)]
#[bw(little, repr = u32)]
#[br(little, repr = u32,)]
pub enum ToTestMessageType {
Message1 = 2,
Message2,
Message3,
Message4 = 16,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub struct TestMessageValue {
pub id: String,
#[serde(rename = "companyCode")]
pub code: TestCode,
pub data: Option<String>,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub enum TestCode {
TestInfo,
#[serde(rename = "Company1")]
Company1,
#[serde(rename = "Company2")]
Company2,
}
/// Custom writer for both length and value.
#[binrw::writer(writer, endian)]
fn custom_writer(value: &TestMessageValue) -> BinResult<()> {
let pos = writer.stream_position()?;
// Serialize
let mut msg_value = serde_json::to_vec(value).map_err(|e| binrw::Error::Custom {
pos,
err: Box::new(e),
})?;
msg_value = format_send_msg_value(msg_value);
// Get value length, and add 1(the length of '0x0d' byte).
let length = msg_value.len() as u32;
// Write value length first.
length.write_options(writer, endian, ())?;
// Then write value
msg_value.write_options(writer, endian, ())
}
/// Custom reader for both length and value.
#[binrw::parser(reader, endian)]
fn custom_reader() -> BinResult<TestMessageValue> {
// Read value length.
let length = u32::read_options(reader, endian, ())?;
let pos = reader.stream_position()?;
let mut msg_value = vec![0; length as usize];
// Read value.
reader.read_exact(&mut msg_value)?;
msg_value = format_receive_msg_value(msg_value).map_err(|e| binrw::Error::Custom {
pos,
err: Box::new(e),
})?;
// Deserialize
serde_json::from_slice::<TestMessageValue>(&msg_value).map_err(|e| binrw::Error::Custom {
pos,
err: Box::new(e),
})
}
// Format send message value. Take ownership for performance, and we won't use original message
// value later.
fn format_send_msg_value(mut msg_value: Vec<u8>) -> Vec<u8> {
// Refers to doc 2.5.2, replace these bytes
msg_value = msg_value
.into_iter()
.flat_map(|v| match v {
0x0e => vec![0x0e, 0x01],
0x0d => vec![0x0e, 0x02],
_ => vec![v],
})
.collect::<Vec<_>>();
// Doc 2.5.1
msg_value.push(0x0d);
msg_value
}
// Format receive message value. Take ownership for performance, and we won't use original message
// value later.
fn format_receive_msg_value(mut msg_value: Vec<u8>) -> anyhow::Result<Vec<u8>> {
if let Some(end_byte) = msg_value.pop()
&& end_byte != 0x0d
{
anyhow::bail!("Error: Message do not end with 0x0d");
};
let mut result = Vec::new();
let mut iter = msg_value.into_iter().peekable();
while let Some(&v) = iter.peek() {
iter.next();
match v {
0x0e_u8 => {
if let Some(&next_v) = iter.peek() {
if next_v == 0x01_u8 {
result.push(0x0e); // Replace [0x0e, 0x01] with [0x0e]
iter.next(); // Consume the next element (0x01)
continue;
}
if next_v == 0x02_u8 {
result.push(0x0d); // Replace [0x0e, 0x02] with [0x0d]
iter.next(); // Consume the next element (0x02)
continue;
}
result.push(v);
}
}
_ => result.push(v),
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn serde_json_to_string() {
let json = r#"{"id":"01466d6c-496c-4b9d-a34b-c0f1cfc47ca4","companyCode":"Company1","data":"extra-data"}"#;
let message_value = serde_json::from_str::<TestMessageValue>(json).unwrap();
assert_eq!(
message_value,
TestMessageValue {
id: "01466d6c-496c-4b9d-a34b-c0f1cfc47ca4".to_string(),
code: TestCode::Company1,
data: Some("extra-data".to_string())
}
);
let json2 = serde_json::to_string(&message_value).unwrap();
assert_eq!(json, json2);
}
#[test]
fn binary_send() {
let msg = ToTestMessage {
r#type: ToTestMessageType::Message1,
value: TestMessageValue {
id: "01466d6c-496c-4b9d-a34b-c0f1cfc47ca4".to_string(),
code: TestCode::Company1,
data: Some("extra-data".to_string()),
},
};
let mut writer = Cursor::new(Vec::new());
msg.write(&mut writer).unwrap();
let bytes = writer.into_inner();
assert_eq!(
bytes,
vec![
0x02, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x69, 0x64, 0x22, 0x3a,
0x22, 0x30, 0x31, 0x34, 0x36, 0x36, 0x64, 0x36, 0x63, 0x2d, 0x34, 0x39, 0x36, 0x63,
0x2d, 0x34, 0x62, 0x39, 0x64, 0x2d, 0x61, 0x33, 0x34, 0x62, 0x2d, 0x63, 0x30, 0x66,
0x31, 0x63, 0x66, 0x63, 0x34, 0x37, 0x63, 0x61, 0x34, 0x22, 0x2c, 0x22, 0x63, 0x6f,
0x6d, 0x70, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x43, 0x6f,
0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31, 0x22, 0x2c, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22,
0x3a, 0x22, 0x65, 0x78, 0x74, 0x72, 0x61, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7d,
0x0d,
]
);
let mut cursor = Cursor::new(bytes);
let msg_receive = ToTestMessage::read(&mut cursor).unwrap();
assert_eq!(msg, msg_receive);
}
#[test]
fn binary_receive() {
let msg = FromTestMessage {
r#type: FromTestMessageType::Message6,
value: TestMessageValue {
id: "af0a2097-8443-4545-93aa-c6aa5ae25b4a".to_string(),
code: TestCode::Company2,
data: None,
},
};
// let mut writer = Cursor::new(Vec::new());
// msg.write(&mut writer).unwrap();
// let bytes = writer.into_inner();
let b = vec![
0x0a, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x69, 0x64, 0x22, 0x3a,
0x22, 0x61, 0x66, 0x30, 0x61, 0x32, 0x30, 0x39, 0x37, 0x2d, 0x38, 0x34, 0x34, 0x33,
0x2d, 0x34, 0x35, 0x34, 0x35, 0x2d, 0x39, 0x33, 0x61, 0x61, 0x2d, 0x63, 0x36, 0x61,
0x61, 0x35, 0x61, 0x65, 0x32, 0x35, 0x62, 0x34, 0x61, 0x22, 0x2c, 0x22, 0x63, 0x6f,
0x6d, 0x70, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x22, 0x43, 0x6f,
0x6d, 0x70, 0x61, 0x6e, 0x79, 0x32, 0x22, 0x2c, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22,
0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x7d, 0x0d,
];
// assert_eq!(
// bytes,
// b
// );
let mut cursor = Cursor::new(b);
let msg_receive = FromTestMessage::read(&mut cursor).unwrap();
assert_eq!(msg, msg_receive);
}
#[test]
fn binary_send_and_receive() {
let send_message = TestMessage {
r#type: TestMessageType::HeartBeat,
value: TestMessageValue {
id: "test-id".to_string(),
code: TestCode::Company1,
data: None,
},
};
// Write message to binary.
let mut writer = Cursor::new(Vec::new());
send_message.write(&mut writer).unwrap();
// Read message from binary.
let bytes = writer.into_inner();
// println!("{bytes:?}");
let mut cursor = Cursor::new(bytes);
let receive_message = TestMessage::read(&mut cursor).unwrap();
assert_eq!(send_message, receive_message);
}
#[test]
fn send_message_value_format() {
let mut message_value = vec![0x01, 0x02, 0x03, 0x04, 0x0d, 0xe, 0x0f, 0x10];
message_value = format_send_msg_value(message_value);
assert_eq!(
message_value,
vec![
0x01, 0x02, 0x03, 0x04, 0x0e, 0x02, 0x0e, 0x01, 0x0f, 0x10, 0x0d
]
)
}
#[test]
fn receive_message_value_format() {
let mut message_value = vec![0x05, 0x06, 0x0e, 0x02, 0xf0, 0x0d];
message_value = format_receive_msg_value(message_value).unwrap();
assert_eq!(message_value, vec![0x05, 0x06, 0x0d, 0xf0])
}
}