< 返回版块

OU_L 发表于 2021-06-16 14:57

新人小白请教各位大佬一个问题,

我想在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。

评论区

写评论
作者 OU_L 2021-06-16 19:33

是的这样也可以,但是我找到了新的更合适的方法,谢谢你的建议~

--
👇
ywxt: 如果Message只有三种情况,那直接传入Message不就行了。

作者 OU_L 2021-06-16 19:31

非常感谢大佬不厌其烦地解释。受益匪浅。

我后来仔细想了想,我发现其实是我想法走偏了,枚举方法是对的,但是对我来说还是比较麻烦,我看了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(),
        }
    }
}
ruby 2021-06-16 18:15

简单来说,传输层就是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(),
        }
    }
}
ruby 2021-06-16 18:05

我在上个帖子都已经给你能运行的示例代码了,肯定是把整个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(())
}
ywxt 2021-06-16 15:35

如果Message只有三种情况,那直接传入Message不就行了。

1 共 5 条评论, 1 页