TCP客户端消息重叠问题求助:解决输入时消息覆盖故障
问题修复方案
问题根源
当用户在终端输入文本时,光标停留在输入行中间,此时直接用print!输出收到的消息,终端会将消息插入到当前光标位置,与用户未完成的输入内容重叠,导致显示混乱。
修复方法:使用crossterm库处理终端交互
crossterm是Rust跨平台终端操作库,能轻松处理光标移动、行清除等控制,完美解决输入输出冲突问题。
步骤1:添加依赖
在Cargo.toml中加入以下依赖:
[dependencies] tokio = { version = "1.0", features = ["full"] } rodio = "0.17" crossterm = "0.27"
步骤2:修改客户端代码
use std::io::Write; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpStream; use tokio::sync::mpsc; use crossterm::{ cursor::{SavePosition, RestorePosition}, event::{self, Event, KeyCode, KeyEvent}, terminal::{Clear, ClearType}, ExecutableCommand, QueueableCommand, }; #[tokio::main] async fn main() { let mut stream = TcpStream::connect("localhost:8080").await.unwrap(); let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap(); let (tx, mut rx) = mpsc::channel(10); // 用crossterm监听键盘输入,实时维护输入缓冲区 tokio::spawn(async move { let mut stdout = std::io::stdout(); let mut input_buf = String::new(); loop { if let Ok(Event::Key(KeyEvent { code, .. })) = event::read() { match code { KeyCode::Char(c) => { input_buf.push(c); stdout.write_all(c.to_string().as_bytes()).unwrap(); stdout.flush().unwrap(); } KeyCode::Backspace => { if !input_buf.is_empty() { input_buf.pop(); stdout.queue(crossterm::cursor::MoveLeft(1)).unwrap(); stdout.queue(Clear(ClearType::UntilNewLine)).unwrap(); stdout.flush().unwrap(); } } KeyCode::Enter => { let line = input_buf.clone() + "\n"; tx.send(line).await.unwrap(); input_buf.clear(); writeln!(stdout, "\nsended!").unwrap(); stdout.flush().unwrap(); } _ => {} } } } }); let (reader, mut writer) = stream.split(); let mut reader = BufReader::new(reader); let mut stdout = std::io::stdout(); let mut line = String::new(); loop { line.clear(); tokio::select! { result = reader.read_line(&mut line) => { result.unwrap(); // 处理收到的消息,避免干扰用户输入 stdout.execute(SavePosition).unwrap(); stdout.execute(Clear(ClearType::CurrentLine)).unwrap(); writeln!(stdout, "\r{}", line.trim_end()).unwrap(); stdout.execute(RestorePosition).unwrap(); stdout.flush().unwrap(); // 播放提示音 let file = std::fs::File::open("beep.wav").unwrap(); let beep1 = stream_handle.play_once(std::io::BufReader::new(file)).unwrap(); beep1.set_volume(0.2); } result = rx.recv() => { let line_write = result.unwrap(); writer.write_all(line_write.as_bytes()).await.unwrap(); } } } }
代码说明
- 输入监听:用
crossterm::event替代原stdin.read_line,实时记录用户输入缓冲区,支持退格、回车等操作。 - 消息输出控制:收到服务器消息时:
- 保存当前光标位置
- 清除当前输入行
- 换行打印新消息
- 恢复光标到输入位置
- 刷新终端确保显示正确
- 异步上下文统一:将原
std::thread::spawn替换为tokio::spawn,保持异步环境一致性。
内容的提问来源于stack exchange,提问作者teilai teilai
相关产品推荐
相关产品推荐

