我正在尝试生成一个服务器并在另一个线程上连接它。我知道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) }
}
}
}
答案 0 :(得分:4)
首先,不要忽略警告。您有4个warning: unused result which must be used
类型的错误。其中每一个都可能是您的代码失败而您甚至不知道的情况。宽大地使用expect
!
其次,你有一个开放的客户端读取套接字,你要求"读取所有数据,直到结束为字符串"。 决定结束的是什么?在这种情况下,套接字关闭时;那是什么时候?
欺骗问题!
那么什么时候发生?因为没有具体的代码,它会在套接字被删除时关闭,所以:
stream.shutdown(std::net::Shutdown::Write).expect("could not shutdown");
第三,你写的是BufWriter
。查看相关文档:
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的恭维数。
另见: