首页 > 解决方案 > UnixStream 的 read_to_end 方法永远不会返回

问题描述

我正在使用 Unix 域套接字来通信进程的应用程序中工作。为此,我使用标准库中的UnixListenerandUnixStream类型。

我发现根据我读取数据的方式,进程可能会挂起。调试后我发现这取决于我是使用read还是read_to_end从套接字中检索数据。usingread工作正常,而 usingread_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.但客户端进程什么也不打印,直到服务器进程被杀死,此时它打印其预期输出:CLIENT: hello_world received from server.

要确认此问题已本地化到该read_to_end方法,请取消注释标记为工作变体的行并注释标记为使示例正常工作的行。

这段代码有什么问题?我用read_to_end错了吗?

标签: rustunix-socket

解决方案


推荐阅读