我正在使用Unix域套接字来交流进程的应用程序。为此,我在标准库中使用UnixListener
和UnixStream
类型。
我发现根据我读取数据的方式,该过程可能会挂起。调试后我发现它取决于我是使用read
还是read_to_end
从套接字中检索数据。使用read
工作正常,而使用read_to_end
会使调用进程挂起,直到我终止其他进程(此时数据实际被读取)。这似乎是刷新缓冲区的问题,尽管文档没有提到任何类型。
这是问题的一个有效例子:
use std::env;
use std::io::{Read, Write};
use std::os::unix::net::{UnixListener, UnixStream};
use std::thread;
fn handle_client(mut stream: UnixStream) {
loop {
let mut read = [0; 1028];
match stream.read(&mut read) {
Ok(n) => {
if n == 0 {
// connection was closed
break;
}
let msg = String::from_utf8(read.to_vec()).unwrap();
println!("SERVER: {} received from remote client.", msg);
stream.write(&read[0..n]).unwrap();
}
Err(err) => {
panic!(err);
}
}
}
println!("SERVER: Ending connection with client.");
}
fn start_server(address: String) {
let listener = UnixListener::bind(&address).unwrap();
// accept connections and process them, spawning a new thread for each one
for connection in listener.incoming() {
match connection {
Ok(stream) => {
/* connection succeeded */
thread::spawn(|| handle_client(stream));
}
Err(err) => {
/* connection failed */
println!("Error connecting to client: {}", err);
break;
}
}
}
}
fn start_client(address: String, msg: String) {
let mut socket = match UnixStream::connect(&address) {
Ok(sock) => sock,
Err(e) => {
println!("Couldn't connect! {}.", e);
return;
}
};
let request = msg.into_bytes();
match socket.write_all(&request) {
Ok(_) => {}
Err(e) => println!("Error writing to socket: {}", e),
}
// This variant works
// let mut read = [0; 1028];
// match socket.read(&mut read) {
// This variant does not work.
let mut read = Vec::with_capacity(1024); //Comment this to make it work
match socket.read_to_end(&mut read) {
// Comment this to make it work
Ok(n) => {
if n == 0 {
// connection was closed
}
let msg = String::from_utf8(read.to_vec()).unwrap();
println!("CLIENT: {} received from server.", msg);
}
Err(err) => {
panic!(err);
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 2 {
let operation_mode = args[1].clone();
let address = args[2].clone();
if operation_mode == "server" {
start_server(address);
} else {
let msg = args[3].clone();
start_client(address, msg);
}
} else {
println!("not enough parameters");
}
}
要重现此问题,请运行如下服务器进程:rm /tmp/sock & RUST_BACKTRACE=1 cargo run server /tmp/sock
以及之后的客户端进程,如下所示:cargo run client /tmp/sock hello_world
问题再现时的预期输出是服务器进程按预期打印其输出:SERVER: hello_world received from remote client.
但客户端进程不打印任何内容,直到服务器进程被终止,此时它将打印其预期输出:{ {1}}
要确认此问题已本地化为CLIENT: hello_world received from server.
方法,请取消注释标记为工作变体的行,并对标记为示例的行进行注释。
此代码有什么问题?我使用read_to_end
错了吗?