使用Serde反序列化来自套接字的换行分隔JSON

5

我正在尝试使用serde将JSON结构从客户端发送到服务器。来自客户端的换行表示套接字已完成。我的服务器看起来像这样:

#[derive(Serialize, Deserialize, Debug)]
struct Point3D {
    x: u32,
    y: u32,
    z: u32,
}

fn handle_client(mut stream: TcpStream) -> Result<(), Error> {
    println!("Incoming connection from: {}", stream.peer_addr()?);
    let mut buffer = [0; 512];
    loop {
        let bytes_read = stream.read(&mut buffer)?;
        if bytes_read == 0 {
            return Ok(());
        }
        let buf_str: &str = str::from_utf8(&buffer).expect("Boom");
        let input: Point3D = serde_json::from_str(&buf_str)?;
        let result: String = (input.x.pow(2) + input.y.pow(2) + input.z.pow(2)).to_string();
        stream.write(result.as_bytes())?;
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        eprintln!("Please provide --client or --server as argument");
        std::process::exit(1);
    }
    if args[1] == "--server" {
        let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
        for stream in listener.incoming() {
            match stream {
                Err(e) => eprintln!("failed: {}", e),
                Ok(stream) => {
                    thread::spawn(move || {
                        handle_client(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                    });
                }
            }
        }
    } else if args[1] == "--client" {
        let mut stream = TcpStream::connect("127.0.0.1:8888").expect("Could not connect to server");
        println!("Please provide a 3D point as three comma separated integers");
        loop {
            let mut input = String::new();
            let mut buffer: Vec<u8> = Vec::new();
            stdin()
                .read_line(&mut input)
                .expect("Failed to read from stdin");
            let parts: Vec<&str> = input.trim_matches('\n').split(',').collect();
            let point = Point3D {
                x: parts[0].parse().unwrap(),
                y: parts[1].parse().unwrap(),
                z: parts[2].parse().unwrap(),
            };
            stream
                .write(serde_json::to_string(&point).unwrap().as_bytes())
                .expect("Failed to write to server");

            let mut reader = BufReader::new(&stream);

            reader
                .read_until(b'\n', &mut buffer)
                .expect("Could not read into buffer");
            print!(
                "{}",
                str::from_utf8(&buffer).expect("Could not write buffer as string")
            );
        }
    }
}

在读取字符串之前,我如何知道要分配多长的缓冲区?如果我的缓冲区过大,则serde会出现反序列化错误,提示存在无效字符。有更好的方法吗?


我不知道这个存在。我尝试了这个 let input: Point3D = serde_json::from_reader(&stream)?;,看起来它只是等待客户端的 EOF。我该如何让 from_reader 在客户端发送特殊字符时退出? - ACC
看了这个例子 https://docs.serde.rs/serde_json/fn.from_reader.html 我认为 from_reader 在读取文件时会在遇到 EOF 时退出,EOF 是读取文件时的特殊字符。我想知道是否需要将换行符视为特殊字符,并在获取到换行符时使 from_reader 返回。或者我对此有误解吗? - ACC
EOF不是“字符”。你从未回答过这个问题:作为程序员,你如何知道socket何时“完成”? 你正在定义一个协议,但你还没有分享你的协议具体是什么,所以我们无法告诉你如何实现它。换行符是否与通过网络传输的数据有关系? - Shepmaster
我正在尝试使用来自客户端到服务器的换行符来标记套接字已完成。抱歉表述不够清晰。为了提供一些背景,我添加了我的服务器和客户端代码。 - ACC
你似乎已经知道了.read_until的用法,为什么在这里不适用呢? - Shepmaster
我用read_until让它工作了。感谢你的指引! - ACC
1个回答

5

TcpStream 放入一个 BufReader 中。这样可以读取直到特定字节(在本例中为换行符)。然后您可以使用 Serde 解析已读取的字节:

use std::io::{BufRead, BufReader};
use std::io::Write;

fn handle_client(mut stream: TcpStream) -> Result<(), Error> {
    let mut data = Vec::new();
    let mut stream = BufReader::new(stream);

    loop {
        data.clear();

        let bytes_read = stream.read_until(b'\n', &mut data)?;
        if bytes_read == 0 {
            return Ok(());
        }

        let input: Point3D = serde_json::from_slice(&data)?;
        let value = input.x.pow(2) + input.y.pow(2) + input.z.pow(2);

        write!(stream.get_mut(), "{}", value)?;
    }
}

为了再利用data的分配,我使用了一些技巧,这意味着在每次循环开始时重置缓冲区非常重要。我还避免为结果分配内存,直接输出到输出流中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接