已解决
不存在 一劳永逸
的流读取方法,应针对不同通信协议制定不同的流读取方法
当缓冲区 buf
的大小恰好与io流中的数据大小相等时,下面的函数将不会返回一直卡死。应该如何解决?
use std::io;
fn read_once<R: ?Sized + io::Read>(reader: &mut R) -> io::Result<Vec<u8>> {
let mut result = Vec::new();
let mut buf = [0; 128];
loop {
let len = match reader.read(&mut buf) {
Ok(0) => break,
Ok(len) => len,
Err(e) => return Err(e),
};
result.append(&mut buf[..len].to_vec());
if len < buf.len() {
break;
}
}
Ok(result)
}
我用回声程序来测试可触发bug, 仓库链接
1
共 9 条评论, 1 页
评论区
写评论一般是读取一定长度就可以分辨其所属的协议(根据协议头以及其它特征),再由相关的协议handler处理后续的流程。
shadowsocks windows客户端的入口是采用的同端口http.socks5协议识别。 gost也支持同端口多协议。
可以看看这两个以及其它项目,看看它们是如何实现的。
谢谢,受益匪浅。我其实在尝试做代理,但在对同一端口支持多协议和加密通讯这里绕进去了。
协商
是重点,不应该试图读取全部数据,而是要针对不同的协议逐一去识别、读取。--
👇
me1ting: 补充
从你函数逻辑来讲,其退出循环的条件是:
当你发送的数据是非缓冲区大小整数倍的,因为退出了循环,所以client可以继续执行。但是当其长度是缓冲区大小整数倍时,进入循环,由于client的发送、回显是单线程执行的,循环没有退出,又无法发送数据从而中断循环,因此阻塞在了读取数据上。
网络编程都是采取协议来进行协商的,不是凭心情来发送、接收数据,你可以进一步学习《计算机网络》,并尝试一些网络编程练习,比如了解HTTP代理、SOCKS5代理,编写一个网络代理软件。
补充
从你函数逻辑来讲,其退出循环的条件是:
当你发送的数据是非缓冲区大小整数倍的,因为退出了循环,所以client可以继续执行。但是当其长度是缓冲区大小整数倍时,进入循环,由于client的发送、回显是单线程执行的,循环没有退出,又无法发送数据从而中断循环,因此阻塞在了读取数据上。
网络编程都是采取协议来进行协商的,不是凭心情来发送、接收数据,你可以进一步学习《计算机网络》,并尝试一些网络编程练习,比如了解HTTP代理、SOCKS5代理,编写一个网络代理软件。
“我的需求是从io流中接收任意大小的数据并返回” 你缺乏对流模型的理解,而这是与语言无关的,或者说是系统底层提供的抽象
read()方法本来就是读取一次,无论是阻塞读还是非阻塞读
可以多看看API,了解下底层,多思考,同时参考其它语言的API进行了解,比如Golang的文档就很简单清晰
所以你这代码根本实现不了你的需求。
而且没有代码能满足你的需求,因为你理想中的读一次是用一个无限大的缓冲区来接受数据,并返回。但是谁也无法预先知道会有多少数据发送来,该用多大的缓冲区来接收。而且因为底层存在的缓冲,数据发送的时间间隔是并不可靠的,这也是为什么,所有语言的API都是这样设计:
read
如果读取到EOF会返回0,按照代码逻辑直接break循环了,那么应该不是一直循环导致的卡死,可能是read一直没有返回?建议开启断点调试。https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=144da5300d4f9264b1eb4d321ae31036
你是怎么卡死的?
我测试时候发现返回值很正常
这功能看着像
read_to_end
https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_end
我看了一下
read_exact
的源码,它的作用是将buf
缓冲区填满,貌似不太符合我的需求。 我的需求是从io流中接收任意大小的数据
并返回。。我的read_once
函数运行的不错,唯独当接收到的数据和缓冲区一样大小时就出bug了...--
👇
uno: 去看read_exact的代码就知道了
去看read_exact的代码就知道了