如何阻止Telnet客户端回显?Rust+Tokio TCP聊天服务器问题
解决MacOS Telnet连接Tokio TCP聊天服务器时的回显问题
核心原因
MacOS自带的Telnet默认使用「过时行模式」,客户端会默认回显输入内容。你之前尝试的IAC WONT ECHO方向搞反了——Telnet选项协商的逻辑是:
IAC WILL ECHO:服务器告知客户端「我来负责回显,你关闭自身回显」IAC WONT ECHO:服务器告知客户端「我不负责回显,你自己处理」
发送错误的命令才会导致无效或报错。
具体修改步骤
基于Tokio官方chat示例,需要做两处修改:
1. 连接建立后发送正确的Telnet回显控制命令
在处理客户端连接的process函数开头,先向Socket发送IAC WILL ECHO序列,告知客户端关闭自身回显:
use tokio::io::{AsyncReadExt, AsyncWriteExt}; // 定义Telnet常量 const IAC: u8 = 255; const WILL: u8 = 251; const ECHO: u8 = 1; async fn process(mut socket: tokio::net::TcpStream) -> Result<(), Box<dyn std::error::Error>> { // 首先发送Telnet回显控制命令 socket.write_all(&[IAC, WILL, ECHO]).await?; // 原示例的后续代码... let mut buf = [0; 1024]; loop { let n = socket.read(&mut buf).await?; if n == 0 { return Ok(()); } // ... } }
2. 过滤客户端发来的Telnet协商命令
客户端收到服务器的WILL ECHO后,会回复IAC DO ECHO确认,这些字节会被当成聊天内容读取,导致乱码或报错。需要在读取后过滤掉Telnet命令序列:
// 新增过滤函数 fn filter_telnet_commands(input: &[u8]) -> Vec<u8> { let mut output = Vec::new(); let mut iter = input.iter().peekable(); while let Some(&byte) = iter.next() { if byte == IAC { // 跳过IAC后的命令字节和选项字节(共2个) iter.next(); iter.next(); } else { output.push(byte); } } output } // 在读取数据后应用过滤 async fn process(mut socket: tokio::net::TcpStream) -> Result<(), Box<dyn std::error::Error>> { socket.write_all(&[IAC, WILL, ECHO]).await?; let mut buf = [0; 1024]; loop { let n = socket.read(&mut buf).await?; if n == 0 { return Ok(()); } // 过滤Telnet命令,只保留实际聊天内容 let filtered_data = filter_telnet_commands(&buf[0..n]); if filtered_data.is_empty() { continue; } // 后续使用filtered_data代替原buf内容 let msg = String::from_utf8_lossy(&filtered_data); // 原示例中的广播逻辑... } }
效果验证
修改后重新启动服务器,用Telnet连接:
telnet localhost 6142
此时输入内容不会被客户端回显,只有当你按下回车发送后,服务器广播的消息(包括你自己的输入)会显示出来,同时保持了行模式的输入方式。
内容的提问来源于stack exchange,提问作者James Mclaughlin
相关产品推荐
相关产品推荐

