我正在尝试生成一个服务器并在不同的线程上连接到它。我知道 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保留数据的内存缓冲区,并以大的、不频繁的批次将其写入底层编写器。当写入器被删除时,缓冲区将被写出。
将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 的补码数。
也可以看看:
| 归档时间: | 
 | 
| 查看次数: | 3099 次 | 
| 最近记录: |