我可以很容易地在Rust中创建一个TcpListener和TcpAcceptor,但我不能完全了解他们的工作。看起来监听器并没有真正倾听,只是接受者的设置结构。
let tcp_listener = TcpListener::bind(addr);
let mut acceptor = tcp_listener.listen();
drop(acceptor);
令我困惑的一件事就是掉线。我不知道drop定义在哪里,我对标准库进行了一些搜索,但我只能将其作为特征方法。
为什么放弃接受者会停止听?难道不应该只停止接受吗?当然,你想要放弃听众。为什么甚至有一个TcpListener,因为接受器似乎做了一切?
答案 0 :(得分:7)
简而言之,在BSD套接字框架中,这里通常是服务器的启动方式:
socket()
系统调用创建主服务器套接字; bind()
系统调用绑定到特定地址/端口; listen()
系统调用;从这一刻起,直到主套接字关闭,它将接管它所绑定的端口,并将监听传入的连接; accept()
;此调用将阻塞,直到某个客户端连接,然后它将返回一个新的套接字,表示与该客户端的连接;与该客户端的所有通信都通过此套接字;当交互结束时,应关闭此套接字。Rust中TCP堆栈使用的主要方法的命名遵循标准的BSD套接字命名,但是,Rust API更抽象,因此隐藏了一些低级概念。
TcpListener::bind()
创建一个新的主套接字(socket()
系统调用)并将其绑定到提供的地址(bind()
系统调用)。它返回一个对象TcpListener
,它将主套接字封装在"创建,但不活动"状态。
TcpListener
提供了一种称为TcpListener::listen()
的方法,它包裹listen()
系统调用,并且"激活"服务器套接字它返回一个新对象TcpAcceptor
,它为接受传入连接提供了方便的接口。它表示"创建并激活"状态。
TcpAcceptor
反过来有几个方法(TcpAcceptor::incoming()
和TcpAcceptor::accept()
),它们包装accept()
系统调用并阻止当前任务,直到某个客户端建立连接导致{ {1}} - 表示客户端套接字的对象。
调用析构函数时,将释放与所有套接字关联的所有OS资源。 Rust中的析构函数可以与任何自定义结构相关联。要在结构中添加析构函数,必须为其实现TcpStream
特征。当此类结构的实例超出范围时,将调用析构函数,例如:
Drop
此程序打印struct Test { value: int }
impl Drop for Test {
fn drop(&mut self) {
println!("Dropping Test: value is {}", self.value);
}
}
fn main() {
let test = Test { value: 10 };
// main ends, `test` goes out of scope
}
。
对于在其上实施Dropping Test: value is 10
特征的结构,重要的是它们不能隐式复制。这意味着当这些结构的实例传递给函数参数或分配给不同的变量时,它们从原始位置移出,也就是说,您不能再通过它们的原始变量使用它们。例如(使用上面的Drop
定义):
Test
困扰你的 let test = Test { value: 10 };
let test2 = test;
println!("{}", test.value); // error: use of moved value: `test`
函数在drop()
模块中定义; here是其文档。它的实现非常简单;它看起来像这样:
core::mem
它什么都不做!但是因为它按值接受它的参数,所以该参数被移入fn drop<T>(x: T) { }
调用。然后所述参数立即超出范围,因为函数返回。如果drop()
有与之关联的析构函数,它将在此处运行。因此,T
本质上是一个&#34;黑洞&#34;:你放入它的所有内容都被销毁(除非它是隐式可复制的,即)。语言的非常好的属性是这样的函数可以用简单的Rust编写,没有任何魔法。
实际上,drop()
几乎没有用,因为当相应的对象超出范围时,所有析构函数总是运行。当需要一些具有可破坏资源的复杂逻辑时偶尔会有用,但它很少发生。