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

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();
            }
        }
    }
}

代码说明

  1. 输入监听:用crossterm::event替代原stdin.read_line,实时记录用户输入缓冲区,支持退格、回车等操作。
  2. 消息输出控制:收到服务器消息时:
    • 保存当前光标位置
    • 清除当前输入行
    • 换行打印新消息
    • 恢复光标到输入位置
    • 刷新终端确保显示正确
  3. 异步上下文统一:将原std::thread::spawn替换为tokio::spawn,保持异步环境一致性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:12:03