You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust CLI应用:终端调整致阻塞WebSocket中断(os error 4)问题求助

解决方案:处理SIGWINCH中断同步WebSocket调用

问题根源

SIGWINCH信号会中断所有阻塞系统调用,你的tungstenite::read_message()属于阻塞调用,终端调整时会触发os error 4(EINTR)。另外,手动忽略SIGWINCH的做法会破坏ncurses的默认信号处理逻辑——ncurses原本会捕获SIGWINCH并自动更新终端尺寸,忽略它不仅会导致尺寸同步失效,信号依然会中断系统调用。

可行方案

1. 恢复ncurses的SIGWINCH默认处理

删除你添加的忽略SIGWINCH的代码,让ncurses自行处理该信号。ncurses会在收到SIGWINCH时自动更新内部终端尺寸状态,无需手动维护尺寸同步。

2. 重试被中断的WebSocket读取操作

当read_message()返回EINTR错误时,不要视为连接异常,而是重新调用read_message()继续等待服务器消息,这是处理信号中断阻塞系统调用的标准做法。

3. 用ncurses内置函数获取终端尺寸

替换term_size crate的调用,改用ncurses的getmaxyx()函数获取终端尺寸——ncurses在处理SIGWINCH后已更新这些值,无需依赖外部库。

修改后的核心代码示例

initscr();
raw();
keypad(stdscr(), true);
noecho();
timeout(0); // 非阻塞获取输入

loop {
    // 处理用户输入
    match getch() {
        27 => { // ESC键退出
            endwin();
            break;
        },
        ch => {
            if let Some(ch) = char::from_u32(ch as u32) {
                if ch != ' ' {
                    // 生成JSON消息(建议用serde_json替代字符串拼接)
                    let msg = format!(r#"{{"message":"{}"}}"#, ch);
                    if let Err(e) = socket.write_message(Message::Text(msg)) {
                        eprintln!("发送消息失败: {}", e);
                    }
                }
            }
        }
    }

    // 处理WebSocket消息,重试EINTR错误
    loop {
        match socket.read_message() {
            Ok(msg) => {
                match msg {
                    Message::Text(msg) => {
                        // 渲染前获取最新终端尺寸
                        let mut rows = 0;
                        let mut cols = 0;
                        getmaxyx(stdscr(), &mut rows, &mut cols);
                        // 基于rows和cols执行响应式渲染逻辑
                        // ...
                    },
                    _ => {}
                }
                break; // 处理完成,退出内层重试循环
            },
            Err(err) => {
                // 判断是否为信号中断错误
                if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
                    if io_err.kind() == std::io::ErrorKind::Interrupted {
                        continue; // 重试读取操作
                    }
                }
                // 处理其他真实错误
                eprintln!("读取WebSocket消息失败: {}", err);
                endwin();
                return;
            }
        }
    }
}

额外建议

  • 使用serde_json库生成JSON消息,避免手动字符串拼接导致的转义错误。
  • 若需要在终端尺寸变化时执行自定义逻辑(比如重置游戏界面布局),可在渲染阶段通过getmaxyx()获取最新尺寸后处理,无需额外捕获SIGWINCH。

内容的提问来源于stack exchange,提问作者Groux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:26:00