将结构体当二进制流传输是做网络编程时传输协议的常用功能。golang语言可以使用包 encoding/binary
实现,例如
go
import (
"encoding/binary"
"os"
)
...
err := binary.Write(f, binary.LittleEndian, p)
...
rust中可以使用 deku
将结构体实例转换为bytes数组。
安装依赖
rust
use anyhow;
use anyhow::bail;
use deku::prelude::*;
use std::net::TcpStream;
use std::{
io::{Read, Write},
};
定义结构体
rust
#[derive(Debug, PartialEq, DekuRead, DekuWrite, Default)]
struct Message {
#[deku(endian = "big")]
msgtype: u32,
#[deku(endian = "big")]
taskid: i32,
#[deku(endian = "big")]
utctime: u32,
#[deku(endian = "big", update = "self.data.len()")]
bodylen: u32,
#[deku(count = "bodylen", endian = "little")]
data: Vec<u8>,
}
注意上面的结构体,整数字段有些是使用大段规则,有些是使用小段规则,可以通过如下的宏实现
rust
#[deku(endian = "big")]`
或
#[deku(endian = "little")]
通常在定义协议时,需要知道协议体的长度,但是协议的body
部分通常是可变的,例如data: Vec<u8>
,只有在结构体初始化时才知道body
的长度,所以这里使用如下宏延迟计算了协议的长度,如下
rust
#[deku(endian = "big", update = "self.data.len()")]
#[deku(count = "bodylen", endian = "little")]
将结构体转换为字节流发送
添加如下宏后
rust
#[derive(Debug, PartialEq, DekuRead, DekuWrite, Default)]
则可以调用 to_bytes()
方法进行转换。
将转换后的二进制发送到tcp服务器,如下
rust
impl Message {
fn send(&self) -> anyhow::Result<()> {
// 将结构体转换为二进制
let binary_data = self.to_bytes()?;
// tcp发送二进制消息
let mut stream =
match TcpStream::connect(self.host) {
Ok(stream) => stream,
Err(e) => {
bail!(
"Couldn't connect to server {} {}",
self.host,
e
)
}
};
stream.write_all(&binary_data)?;
stream.flush()?;
Ok(())
}
}