使用Tokio的frame_delimited时为何出现FrameTooBig错误?
搞定Tokio中的
FrameTooBig错误 嘿,我来帮你捋清楚这个问题的来龙去脉,以及怎么解决它~
为啥会出现这个错误?
你用的length_delimited编解码器,是专门处理带长度前缀的消息帧的——简单说,它要求每个消息开头必须先传一个表示内容长度的字节(比如4字节的大端整数),然后才是实际的消息内容。
但Telnet默认发的是啥?就是普通的文本,输完内容按个回车,根本没有这个长度前缀。当length_delimited试图把你发的文本(比如"hello\n")当成长度前缀解析时,会算出一个超级大的“消息长度”,远远超过它默认的最大帧限制(8MB),于是直接抛出FrameTooBig错误。
解决办法看这里
根据你的使用场景,有几个靠谱的方案:
方案1:换成行分隔的编解码器(最适合Telnet场景)
如果你的需求就是用Telnet发换行分隔的文本,直接把length_delimited换成LinesCodec就行,它专门处理这种按换行分割的文本消息:
extern crate tokio; extern crate tokio_io; use tokio::io; use tokio::net::TcpListener; use tokio::prelude::*; use tokio_io::codec::LinesCodec; fn main() { let addr = "127.0.0.1:12345".parse().unwrap(); let listener = TcpListener::bind(&addr).unwrap(); let server = listener.incoming().for_each(|socket| { // 用LinesCodec处理socket let framed = LinesCodec::new().framed(socket); // 拆分出接收流和发送sink let (sink, stream) = framed.split(); // 处理收到的每一行消息,这里示例是打印出来 let handle_messages = stream.for_each(|line| { println!("收到消息:{}", line); Ok(()) }); // 把任务放到Tokio运行时里 tokio::spawn(handle_messages.map_err(|err| eprintln!("处理出错:{}", err))); Ok(()) }).map_err(|err| eprintln!("监听出错:{}", err)); tokio::run(server); }
方案2:调整length_delimited的参数(如果必须用长度前缀)
如果你确实需要用带长度前缀的消息格式,那得先调整编解码器的最大帧大小,同时得自己写个符合格式的客户端(Telnet没法直接用了):
use tokio_io::codec::length_delimited::Builder; // 自定义Builder,把最大帧大小调整到你需要的数值,比如64KB let codec = Builder::new() .max_frame_length(64 * 1024) .new_codec(); // 用这个自定义codec处理socket let framed = codec.framed(socket);
这种情况下,客户端发送消息时,得先把内容长度转成指定格式的字节(比如大端序的u32),再跟在内容前面一起发。
方案3:自己写个编解码器(适配特殊格式)
如果你的消息格式比较特别,既想兼容Telnet又要满足业务需求,可以实现tokio_io::codec::Codec trait,自定义解析逻辑。
怎么验证?
拿方案1的代码举例:
- 运行你的服务端程序
- 打开终端输入
telnet 127.0.0.1 12345 - 随便输一行文本回车,服务端应该能正常打印出消息,再也不会出现
FrameTooBig的错误啦~
内容的提问来源于stack exchange,提问作者blasrodri
相关产品推荐
相关产品推荐

