一些朋友和我目前正在网站上做一个在线乒乓游戏. 游戏运行良好,但我想做一个Rust CLI应用程序,允许用户玩与在线玩家在网站上.

为此,我使用了钨合金板条的websocket.我还使用ncurses crate获取用户输入,term_size crate获取终端的大小,以适应游戏的渲染方式.

我与websocket服务器的连接正常,但是当我调整终端的大小以测试"响应"是否正常工作时,我得到了一个os错误4,这是中断的系统调用.经过一些研究,我发现当操作系统中断终端时,操作系统会发送一个SIGWINCH信号给进程,中断每个阻塞的系统调用. 我知道在sync crate中有钨合金(所以我的websocket被阻塞了),这就是为什么我 Select 它而不是tokio(这是我的第一个Rust应用程序,我不想在我不熟悉语言的时候用线程和javoc代码使它复杂化).

我真的不知道如何禁用这个信号(我不认为这样做是一个好主意),或者如何以一种不会中断阻塞系统调用的方式处理它.我也不确定禁用SIGWINCH是否不会" destruct "term_size crate.

这是我代码的一部分:

initscr();
raw();
keypad(stdscr(), true);
noecho();
timeout(0);
loop {
    match getch() {
        27 => { // ESC
            endwin();
            break;
        },
        ch => {
            let ch = match char::from_u32(ch as u32) {
                Some(ch) => {
                    ch
                },
                None => ' '
            };
            if ch != ' ' { // Send user input to the server
                socket.write_message(Message::Text(r#"{"message":"{ch}"}"#.to_string().replace("{ch}", &ch.to_string())));
            }
        }
    }
    match socket.read_message() { // THIS IS WHERE THE ERROR OCCURED
        Ok(msg) => match msg {
            Message::Text(msg) => {
                // render the game
            },
            _ => {}
        },
        Err(err) => { ... }
    }
}

我试过了,但什么也没改变:

use std::os::unix::io::RawFd;
use nix::sys::signal::{self, Signal, SigHandler, SigAction, SigSet, SaFlags};
use nix::unistd::Pid;

fn main() {
    let sig_action = SigAction::new(
        SigHandler::SigIgn,
        SaFlags::empty(),
        SigSet::empty(),
    );
    
    unsafe {
        let _ = signal::sigaction(Signal::SIGWINCH, &sig_action).expect("Failed to ignore SIGWINCH");
    }
    
    // The rest of my code are here (like authentification, selection to create or join a game, etc...)

推荐答案

一般来说,优雅地处理EINTR(你得到的错误)是在Unix上编写代码的一部分. 理论上,它可以发生在大多数系统调用,包括大多数形式的I/O.

在某些系统上,可以使用带有SA_RESTART标志的sigaction来重新启动some个系统调用;这些调用取决于操作系统.然而,如果你使用的是终端库,它不是你的代码将捕获SIGWINCH,而是终端库,所以你的代码仍然需要优雅地处理EINTR.

好消息是,如果你有EINTR个,那么没有数据被发送或接收;如果有一些数据发送或接收,那么你会得到一个短的读或写指示部分数量. 所以,如果你得到了EINTR,你只需要在一个循环中重试,直到你得到成功或其他错误. 所以它看起来像这样:

loop {
    match getch() {
        27 => { // ESC
            endwin();
            break;
        },
        ch => {
            let ch = match char::from_u32(ch as u32) {
                Some(ch) => {
                    ch
                },
                None => ' '
            };
            if ch != ' ' { // Send user input to the server
                socket.write_message(Message::Text(r#"{"message":"{ch}"}"#.to_string().replace("{ch}", &ch.to_string())));
            }
        }
    }
    loop {
        match socket.read_message() {
            Ok(msg) => match msg {
                Message::Text(msg) => {
                    // render the game
                },
                _ => {}
            },
            Err(err) if err.kind() == ErrorKind::Interrupted => continue,
            Err(err) => { ... }
        }
        break;
    }
}

Rust相关问答推荐

有条件默认实现

使用pyo3::Types::PyIterator的无限内存使用量

为什么std repeat trait绑定在impl块和关联函数之间?

通过使用光标拖动角来绕其中心旋转矩形

同时从不同线程调用DLL的不同函数会出现分段错误或产生STATUS_STACK_BUFFER_OVERRUN

Tokio_Postgres行上未显示退回特性的生存期,且生命周期 不够长

我如何制作一个变异迭代器来锁定内部数据直到删除?

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

铁 rust ,我的模块介绍突然遇到了一个问题

try 创建随机数以常量

通过写入 std::io::stdout() 输出不可见

闭包返回类型的生命周期规范

Rust: 目标成员属于哪个"目标家族"的列表是否存在?

从 HashMap>, _> 中删除的生命周期问题

内部值发生变化时 Rc 的行为

Rust Serde 为 Option:: 创建反序列化器

是否可以预测堆栈溢出?

Rust,使用枚举从 HashMap 获取值

Abortable:悬而未决的期货?

当特征函数依赖于为 Self 实现的通用标记特征时实现通用包装器