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
相关产品推荐
相关产品推荐

