文章目录

  • 前言
  • 一、序列化是什么?
  • 二、serde_json
  • 1. Cargo.toml配置
  • 2. 序列化的简单使用
  • 3. 将序列化引入网络编程
  • 总结



前言

本文记录了笔者学习序列化以及将其运用在网络编程中的过程,同时还记录了一个由于意外而发生的os报错( broken pipen )。笔者的学习视频来自b站令狐一冲


一、序列化是什么?

百度百科说,序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
从笔者的角度看,则认为序列化其实就是为前后端通信提供了一种信息传递的格式标准,如json格式,yaml格式,简化了通信之间的逻辑设计难度。
下文主要使用json作为示例:


二、serde_json

serde crate 是 serde 生态的核心,serde derive crate 提供必要的工具,使用过程宏来派生 Serialize 和 Deserialize,但是serde只提供序列化和反序列化的框架,具体的操作还需要依赖具体的包如:serde_json和serde_yaml

1. Cargo.toml配置

-> 笔者使用的Crate.io的镜像源地址是上交大镜像网站

[package]
name = "learn_serde"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0.130",features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"

2. 序列化的简单使用

代码如下:

#[derive(Debug, Serialize, Deserialize)]
struct serverConfig {
    workers:u64,
    ignore:bool,
    auth_server:Option<String>,
}

fn main() {
    let config = serverConfig{
        workers: 100,
        ignore: false,
        auth_server: Some("server.io".to_string()),
    };
    //初始化结构体对象

    {
        println!("json:");
        let serialized = serde_json::to_string(&config).unwrap();
        //序列化结构体
        println!("serialized: {}",serialized);

        let deserialized:serverConfig = serde_json::from_str(&serialized).unwrap();
        //反序列化结构体
        //这里要将对象结构体的名字跟在deserialized:的后面
        println!("deserialized: {:?}",deserialized);
        //输出反序列化的结构体
    }
    println!("Hello, world!");
}

如上就是序列化单独的简单使用,输出结果如下所示:

root@peryol-ThinkPad-T540p:~/learn_rust/learn_serde# cargo run
   Compiling learn_serde v0.1.0 (/root/learn_rust/learn_serde)
    Finished dev [unoptimized + debuginfo] target(s) in 2.15s
     Running `target/debug/learn_serde`
json:
serialized: {"workers":100,"ignore":false,"auth_server":"server.io"}
deserialized: serverConfig { workers: 100, ignore: false, auth_server: Some("server.io") }
Hello, world!

3. 将序列化引入网络编程

在网络编程中,通常使用 std::net::{ TcpListener, TcpStream } 进行C端和S端的监听与接收,而在管道中传输的数据,就使用了序列化这一功能,比如将待发送的数据先转变为json数据格式再逐比特发送到管道中进行传送。笔者在学习过程中也使用了如上提到的知识,跟随学习视频编写了简单的一套客户端与服务端进行学习测试,代码如下:

S端:

use serde::{Serialize,Deserialize};
use serde_json;

use std::io::{self,prelude::*,BufReader,Write};
use std::thread;
use std::net::{TcpListener, TcpStream};

#[derive(Debug, Serialize, Deserialize)]
struct bottle {
//这里定义了序列化的参考结构体
    lable:String,
    con1:String,
    con2:String,
    con3:String,
    con4:String,
}

fn handle_client(mut stream:TcpStream) -> std::io::Result<()> {
    println!("Incoming connection from : {}",stream.peer_addr()?);
    //输出C端的ip地址与端口号
    let mut data = Vec::new();
    //创建存放管道中数据的容器
    let mut stream = BufReader::new(stream);
    //建立读取器
    loop {
        println!("********************");
        data.clear();
        //清理容器中存放的数据
        let bytes_read = stream.read_until(b'\n',&mut data)?;
        //定义读取数据时的终止符'\n'同时将数据读入data容器中
        if bytes_read == 0 {
        //同时注意bytes_read被赋予的是读取字节的长度而不是将内容赋予它
            return Ok(());
        }
        //所以这里的作用就是检测读取数据是否为空
        //如果为空直接结束loop循环

        let input: bottle = serde_json::from_slice(&data)?;
        //反序列化input数据
        let lable = "登陆成功".to_string();
        stream.get_mut()
        .write(&(serde_json::to_vec(&(lable)))?)?;
        stream.get_mut()
        .write(&(serde_json::to_vec(&(input.con1)))?)?;
        //设定数据检测点
        stream.get_mut().write(&("\n".as_bytes()))?;
        //将数据写回C端
        stream.get_mut().flush()?;
        //刷新
    }
}

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("0.0.0.0:8080")?;
    for stream in listener.incoming(){
        //let stream = stream.unwrap();
        match stream {
            Err(e) => eprintln!("error:{}",e),
            Ok(stream) => {
                thread::spawn(move || {
                    handle_client(stream)
                    .unwrap_or_else(|error| eprintln!("error:{}",error));
                });
               // handle.join().unwrap();
            },
        }
    }
    //println!("Hello, world!");
    Ok(())
}

C端:

use std::net::TcpStream;
use std::{thread,str};
use std::io::{self,prelude::*,BufReader,Write};

use serde::{Serialize,Deserialize};
use serde_json;

#[derive(Debug, Serialize, Deserialize)]
struct bottle {
//同样的定义结构体
    lable:String,
    con1:String,
    con2:String,
    con3:String,
    con4:String,
}


fn main() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:8080")?;
    //设置访问ip
    loop {
        println!("*************************");
        let mut input = String::new();
        //存放输入数据的变量
        let mut buffer:Vec<u8> = Vec::new();
io::stdin()
            .read_line(&mut input)
            .expect("Failed to read from stdin");
        let parts:Vec<&str> = input.trim_matches('\n').split(',').collect();
        let point = bottle {
            lable: parts[0].to_string(),
            con1: parts[1].to_string(),
            con2: parts[2].to_string(),
            con3: parts[3].to_string(),
            con4: parts[4].to_string(),
        };
        stream
            .write_all(serde_json::to_string(&point).unwrap().as_bytes())
            .expect("Faild to write");
        stream.write_all(b"\n")?;
let mut reader = BufReader::new(&stream);
        reader.read_until(b'\n',&mut buffer)?;
        let input = str::from_utf8(&buffer).unwrap();
        if input == "" {
            eprintln!("Empty response");
        }

        println!("Response:{}",input);
        //stream.flush()?;
    }
    //println!("Hello, world!");
    //Ok(());
}

运行结果:
S端:

Compiling service v0.1.0 (/root/rust_serde/service)
    Finished dev [unoptimized + debuginfo] target(s) in 0.84s
     Running `target/debug/service`
Incoming connection from : 127.0.0.1:40210
********************
********************
********************

C端:

Compiling client v0.1.0 (/root/rust_serde/client)
    Finished dev [unoptimized + debuginfo] target(s) in 0.69s
     Running `target/debug/client`
*************************
1,2,3,4,5
Response:"登陆成功""2"

*************************
1,2,3,4,5
Response:"登陆成功""2"

*************************
^C

如上就是C端和S端的代码,同时Cargo.toml中的配置与1中相同。经测试,可以顺利进行信息的传输接收,这是很正常的结果,但是笔者在过程中不小心将S端的一行代码注释掉了,如下所示:

data.clear();

这句代码位于S端 loop 循环的开头,作用是将原vec容器中的value值全部清空。笔者误将其注释后,第一次的通信可以正常进行,但是第二次的通信则出现了异常如下所示:

S端:

Incoming connection from : 127.0.0.1:41072
********************
********************
error:trailing characters at line 2 column 1

C端:

*************************
1,2,3,4,5
Response:"登陆成功""2"

*************************
1,2,3,4,5
Empty response
Response:
*************************
1,2,3,4,5
Error: Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }

可以发现出现了 BrokenPipe 的 os 报错,出现报错的时候笔者去查阅了相关报错的资料以及rust相关包的官方文档,但是并未发现什么错误,在修正上述错误后,虽然是低级错误,但也引起了笔者的一些思考:

存放数据的vec容器再没有被清理的情况下,是不能被read读入数据的,也就是说所有新传入的数据是被堵塞到管道中未被利用的,这也就能说明出现 BrokenPipe 报错的原因了,传输过去的数据未被利用,没有及时取走管道中的数据,从而系统异常退出。


总结

以上就是笔者在学习过程中遇到的一些问题,关于本篇文章,特别是最后的报错情况,如果各位读者有自己的见解,还请各位不吝赐教!