新人小白请教各位大佬一个问题,
我想在socket中实现一个可以用于各种类型交互的送信收信的函数,送信函数接受一个范型参数x,进行序列化并且发送给对方。收信端收到之后进行反序列化然后返回这个x。
但是我不知道怎么匹配参数x的类型?
这是我的代码:
#[derive(serde::Serialize, serde::Deserialize, Debug)]
enum Message {
I32(i32),
Bool(bool),
String(String)
}
unsafe fn write<A>(socket, x: A)
where A: marker::Send + 'static + serde::Serialize
{
let buffer = match A{ //请教一下这部分怎么实现?或者有没有其他的方案?
i32 =>{ bincode::serialize(&Message::I32(x)).unwrap();},
bool=> {......},
String=>{......},
}
let mut stream = BufWriter::new(socket);
Write::write_all(&mut stream,&buffer).unwrap();
Write::flush(&mut stream).unwrap();
println!("send buffer: {:?}", buffer);
}
unsafe fn read<A>(socket) -> A
where A: marker::Send + 'static
{
let mut buffer:Vec<u8> = [0u8;4].to_vec();
let mut stream = BufReader::new(socket);
let a =Read::read(&mut stream ,&mut buffer).unwrap();
let message = bincode::deserialize(&mut buffer[..]).unwrap();
let result = match message { //还有这里
Message::I32(int) => ...A,
//Message::Bool(bo) => ...A,
//Message::String(str)=> ...A,
_ => panic!("unsupported message type")
};
result
}
想匹配类型的原因是如果序列化的时候没有标注x的具体类型,那么反序列化的时候就会提示出错:
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("invalid value: integer `23`, expected variant index 0 <= i < 2")',
读不太懂这个错误提示,但是我如果序列化的时候是直接声明Message::I32(x),那么就不会报错,但是要求是传递到write函数的参数是范型A,并且read函数的返回值也要求是范型A,所以就想着要匹配一下参数x的类型然后分情况序列化?
感谢各位提供建议和想法~
问题已经解决如下:
unsafe fn write<A>(socket, x: A)
where A: marker::Send + 'static + serde::Serialize
{
let mut stream = BufWriter::new(socket);
Write::write_all(&mut stream,&buffer).unwrap();
Write::flush(&mut stream).unwrap();
println!("send buffer: {:?}", buffer);
}
unsafe fn read<A>(socket) -> A
where A: marker::Send + 'static + DeserializeOwned
{
let mut buffer:Vec<u8> = [0u8;1024].to_vec();
let mut stream = BufReader::new(socket);
let a =Read::read(&mut stream ,&mut buffer).unwrap();
let message:A = bincode::deserialize(&mut buffer[..]).unwrap();
message
}
只需要对范型A添加DeserializeOwned trait。
1
共 5 条评论, 1 页
评论区
写评论是的这样也可以,但是我找到了新的更合适的方法,谢谢你的建议~
--
👇
ywxt: 如果Message只有三种情况,那直接传入Message不就行了。
非常感谢大佬不厌其烦地解释。受益匪浅。
我后来仔细想了想,我发现其实是我想法走偏了,枚举方法是对的,但是对我来说还是比较麻烦,我看了serde bincode文档之后发现一个新方法。
其实在反序列化的时候,将范型A加上DeserializeOwned trait,就可以直接实现对于范型的反序列化了。
这样就不必通过枚举来处理范型A了。
感谢你提供了重要思路,因为我也是刚开始学Rust,所以这些概念没掌握清楚,惭愧惭愧!我也会把枚举方法在实现一遍,加深一下理解。
总之非常感谢你~祝你工作学习顺利~
贴一下我解决后的代码:
unsafe fn write<A>(socket, x: A) where A: marker::Send + 'static + serde::Serialize { let mut stream = BufWriter::new(socket); Write::write_all(&mut stream,&buffer).unwrap(); Write::flush(&mut stream).unwrap(); println!("send buffer: {:?}", buffer); } unsafe fn read<A>(socket) -> A where A: marker::Send + 'static + DeserializeOwned { let mut buffer:Vec<u8> = [0u8;1024].to_vec(); let mut stream = BufReader::new(socket); let a =Read::read(&mut stream ,&mut buffer).unwrap(); let message:A = bincode::deserialize(&mut buffer[..]).unwrap(); message }
还有一些小问题总结: 反序列化的时候,buffer的大小是不一样的,例如整数是4byte,但是string就是22,所以直接往大了整就行。我把buffer设置为 Vec::new() 之后会报错:
`Err` value: Io(Kind(UnexpectedEof))
我尽量往大了设然后就不会了,我觉得可能是一开始设一个空的Vector会导致读不进数据?
还有就是在我这个例子里不能使用read_to_end()函数,要使用read()。
--
👇
ruby: 简单来说,传输层就是
enum Message
的实例序列化成字节流的格式你这写法真是没理解enum
bincode::serialize(&Message::I32(x))
发送端示例:
let request: Vec<u8> = bincode::serialize::<Message>(&Message::I32(66));
接收端示例:
let request: Message = bincode::deserialize::<Message>(bytes);
你如果觉得我的示例过于简陋,你可以学习下actix-web官方代码是怎样用enum+match将websocket通信中7种不同消息类型进行解析,并针对不同类型的消息做不同处理
https://github.com/actix/examples/blob/e5ef02da965519f863511c46adee110b0e9f34aa/websockets/websocket/src/main.rs#L53
以下教科书一般的对WebSocket各种消息类型进行模式匹配的例子抄自actix-web源码
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket { fn handle( &mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context, ) { // process websocket messages println!("WS: {:?}", msg); match msg { Ok(ws::Message::Ping(msg)) => { self.hb = Instant::now(); ctx.pong(&msg); } Ok(ws::Message::Pong(_)) => { self.hb = Instant::now(); } Ok(ws::Message::Text(text)) => ctx.text(text), Ok(ws::Message::Binary(bin)) => ctx.binary(bin), Ok(ws::Message::Close(reason)) => { ctx.close(reason); ctx.stop(); } _ => ctx.stop(), } } }
简单来说,传输层就是
enum Message
的实例序列化成字节流的格式你这写法真是没理解enum
bincode::serialize(&Message::I32(x))
发送端示例:
let request: Vec<u8> = bincode::serialize::<Message>(&Message::I32(66));
接收端示例:
let request: Message = bincode::deserialize::<Message>(bytes);
你如果觉得我的示例过于简陋,你可以学习下actix-web官方代码是怎样用enum+match将websocket通信中7种不同消息类型进行解析,并针对不同类型的消息做不同处理
https://github.com/actix/examples/blob/e5ef02da965519f863511c46adee110b0e9f34aa/websockets/websocket/src/main.rs#L53
以下教科书一般的对WebSocket各种消息类型进行模式匹配的例子抄自actix-web源码
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket { fn handle( &mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context, ) { // process websocket messages println!("WS: {:?}", msg); match msg { Ok(ws::Message::Ping(msg)) => { self.hb = Instant::now(); ctx.pong(&msg); } Ok(ws::Message::Pong(_)) => { self.hb = Instant::now(); } Ok(ws::Message::Text(text)) => ctx.text(text), Ok(ws::Message::Binary(bin)) => ctx.binary(bin), Ok(ws::Message::Close(reason)) => { ctx.close(reason); ctx.stop(); } _ => ctx.stop(), } } }
我在上个帖子都已经给你能运行的示例代码了,肯定是把整个enum Message的实例对象进行序列化传输,你再认真研究下我代码。话说你这一堆unsafe看着没必要,建议看看张老师极客时间的Rust课程,里面都讲了enum是类型构造器,类似C语言tagged Union,你把enum乱用成这样真应该把Rust官方英文的refernce中的enum从头读几遍。
use std::{io::{Read, Write}, os::unix::net::{UnixListener, UnixStream}}; #[derive(serde::Serialize, serde::Deserialize, Debug)] enum Message { I32(i32), Bool(bool) } const UNIX_SOCK_ADDR: &str = "/home/w/temp/my_sock.sock11"; #[test] fn unix_socket_server() -> Result<(), Box<dyn std::error::Error>> { let listener = UnixListener::bind(UNIX_SOCK_ADDR)?; for mut stream in listener.incoming().flatten() { let mut read_buffer = Vec::new(); stream.read_to_end(&mut read_buffer)?; let request: Message = bincode::deserialize(&read_buffer[..])?; println!("Server Incomming Request: {:?}", request); let response = match request { Message::I32(int) => Message::Bool(int % 2 == 1), _ => panic!("unsupported message type") }; println!("Server Preprare Reponse: {:?}", response); // stream.write(&bincode::serialize(&response)?)?; // stream.flush()?; drop(stream); } Ok(()) } #[test] fn unix_socket_client() -> Result<(), Box<dyn std::error::Error>> { let mut stream = UnixStream::connect(UNIX_SOCK_ADDR)?; let request = Message::I32(66); stream.write(&bincode::serialize(&request)?)?; stream.flush()?; Ok(()) }
如果Message只有三种情况,那直接传入Message不就行了。