简单的 Rust TCP 服务器和客户端不接收消息并且永不终止

Simple Rust TCP server and client do not receive messages and never terminates

我正在尝试生成一个服务器并在另一个线程上连接到它。我知道 Rust 有阻塞 I/O,但我觉得我应该能够在不同的线程中连接服务器。我对线程的了解不多。最终游戏是通过网络连接到该服务器。这就是我用 player_stream TCPStream 模拟的内容。 player_stream 会一直等到它的缓冲区中有内容。一旦在那里写入了一些东西,它就会响应回服务器。照原样,程序不会终止。

use std::net::{TcpListener, TcpStream};
use std::io::{BufReader,BufWriter};
use std::io::Write;
use std::io::Read;
use std::thread;

fn main() {
    thread::spawn(move || {
        start_server();
    });
    let player_stream = TcpStream::connect("127.0.0.1:8000").expect("Couldn't connect");
    let mut reader = BufReader::new(&player_stream);
    let mut response = String::new();
    reader.read_to_string(&mut response);
    println!("Player received {}", response);
    let mut writer = BufWriter::new(&player_stream);
    writer.write_all("NAME".as_bytes());
}

fn start_server() {
    let listener = TcpListener::bind("127.0.0.1:8000").unwrap();

    fn handle_client(stream: TcpStream) {
        println!("Client connected");
        let mut writer = BufWriter::new(&stream);
        writer.write_all("Red".as_bytes());
        let mut reader = BufReader::new(&stream);
        let mut response = String::new();
        reader.read_to_string(&mut response);
        println!("Server received {}", response);

    }

// accept connections
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                handle_client(stream);
            }
            Err(e) => { panic!("{}",e) }
        }
    }
}

首先,不要忽略警告。您有 4 个 warning: unused result which must be used 类型的错误。其中每一个都可能是您的代码失败而您 甚至不知道 的情况。自由使用 expect

其次,您有一个打开的客户端读取套接字,并且您要求“将所有数据读取到字符串中直到结束”。 什么决定结束?在这种情况下,它是在套接字关闭时;那是什么时候?

技巧问题!

  • 客户端的读套接字在服务器的写套接字关闭时关闭。
  • 当服务器的读套接字关闭时,服务器的写套接字也关闭。
  • 当客户端的写套接字关闭时,服务器的读套接字也关闭。

那什么时候发生?因为没有专门的代码,所以当套接字被丢弃时它会关闭,所以:

  • 客户端的写套接字在客户端结束时关闭。

于是陷入僵局。这个问题可以通过显式关闭套接字的写入部分来解决:

stream.shutdown(std::net::Shutdown::Write).expect("could not shutdown");

第三,你正在写入BufWriter。查看它的文档:

A BufWriter keeps an in-memory buffer of data and writes it to an underlying writer in large, infrequent batches.

The buffer will be written out when the writer is dropped.

在您尝试阅读响应后,BufWriter 被丢弃在范围的末尾。这是另一个僵局。


最后,您需要建立一个协议来界定如何分隔来回发送的消息。一个简单但非常有限的解决方案是使用 面向行的 协议:每条消息都适合一行,并以换行符结尾。

如果您选择它,则可以改用 read_to_line。我还使用 BufWriter::flush 强制数据通过网络发送;您也可以将 writer 封装在一个块中,以便更早删除它或显式调用 drop(writer).

use std::net::{TcpListener, TcpStream};
use std::io::{BufReader, BufWriter, Write, BufRead};
use std::thread;

fn main() {
    thread::spawn(start_server);

    let player_stream = TcpStream::connect("127.0.0.1:8000").expect("Couldn't connect");

    let mut reader = BufReader::new(&player_stream);
    let mut response = String::new();
    reader.read_line(&mut response).expect("Could not read");
    println!("Player received >{}<", response.trim());

    let mut writer = BufWriter::new(&player_stream);
    writer.write_all("NAME\n".as_bytes()).expect("Could not write");
}

fn start_server() {
    let listener = TcpListener::bind("127.0.0.1:8000").unwrap();

    fn handle_client(stream: TcpStream) {
        println!("Client connected");

        let mut writer = BufWriter::new(&stream);
        writer.write_all("Red\n".as_bytes()).expect("could not write");
        writer.flush().expect("could not flush");

        let mut reader = BufReader::new(&stream);
        let mut response = String::new();
        reader.read_line(&mut response).expect("could not read");
        println!("Server received {}", response);
    }

    for stream in listener.incoming() {
        let stream = stream.expect("Unable to accept");
        handle_client(stream);
    }
}

您会注意到该程序并不总是打印出服务器的响应。那是因为退出的主线程退出了程序


您提到您的真实案例使用 XML,其中可以嵌入换行符,使得面向行的协议不适用。另一种常见的协议是在发送数据本身之前发送一个长度。对此有许多可能的实现。在之前的工作中,我们以这种方式发送了 XML。我们从一个长度在数据本身之前的 ASCII 编码换行符终止字符串开始。在那种情况下,将长度作为字符串的可读性是一个好处。您还可以选择发送一些字节,这些字节可以根据某种字节顺序解释为 2 的补码。


另请参阅: