用Rust实现TCP Echo客户端

151 阅读2分钟

用Rust实现TCP Echo客户端

本文主要讲解《Rustで始めるネットワークプログラミング》一书1.5节的内容——用Rust实现一个TCP Echo客户端。

TCP Echo客户端

虽然用telnet或nc也能够测试(上一节实现的)TCP Echo服务器,但我们还是用Rust创建一个TCP Echo客户端吧。它的主要功能包括:

  • 将从标准输入读入的数据发送到TCP Echo服务器
  • 将从TCP Echo服务器接收到的数据输出到标准输出

TCP Echo客户端的源代码如下所示。

#[macro_use]
extern crate log;
use std::env;
use std::io::{self, BufRead, BufReader, Write};
use std::net::TcpStream;
use std::str;
​
fn main() {
    env::set_var("RUST_LOG", "debug");
    env_logger::init();
​
    let address = "127.0.0.1:1234";
    connect(address).unwrap_or_else(|e| error!("{}", e));
}
​
/**
 * 与指定的IP地址、端口号(所标识的进程)建立TCP连接
 */
pub fn connect(address: &str) -> Result<(), failure::Error> {
    let mut stream = TcpStream::connect(address)?;    // ①
    loop {
        // 将输入的数据通过套接字发送给服务器
        let mut input = String::new();                // ②
        io::stdin().read_line(&mut input)?;
        stream.write_all(input.as_bytes())?;          // ③
​
        // 输出通过套接字接收到的数据
        let mut reader = BufReader::new(&stream);
        let mut buffer = Vec::new();
        reader.read_until(b'\n', &mut buffer)?;
        print!("{}", str::from_utf8(&buffer)?);       // ④
    }
}

TcpStream::connect()①用于与指定的地址通过三次握手建立TCP连接。由于网络中传输的都是字节流,所以输入字符串②在发送之前必须转换成字节切片(byte slice, &[u8])③。反过来,对于接收到的字节切片,需要通过str::from_utf8()将其转换为字符串切片(&str)④。

&[u8]与&str的异同

  • 相同点

    • 二者都是由一个或多个字节(bytes)构成的
  • 不同点

    • 并不是所有的字节切片(byte slices, &[8])都是有效的字符串切片(string slices, &str),因为字符串切片必须是有效的UTF-8的字节序列,例如
    // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c71f7293d16b081d4f3cd9de39ea6883
    fn main() {
        // 汉字“錆”的UTF-8编码是:0xE9 0x8C 0x86
        let array: [u8; 3] = [0xE9, 0x8C, 0x86];
        
        // Slices can be used to borrow a section of an array 
        //   and have the type signature &[T].
        // Slices can point to a section of an array.
        let byte_slice: &[u8] = &array[0..2];   // [0xE9, 0x8C]不是完整的UTF-8编码
        println!("{:?}", byte_slice);           // [233, 140]
        println!("{:?}", std::str::from_utf8(byte_slice));  // Err(Utf8Error ...
        
        
        let byte_slice: &[u8] = &array;
        println!("{:?}", byte_slice);           // [233, 140]
        println!("{:?}", std::str::from_utf8(byte_slice));  // Ok("錆")
    }
    

运行TCP Echo客户端

$ cargo run
welcome to techbookfest6!!! # 发送
welcome to techbookfest6!!! # 接收