Rust中TcpListener和TcpAcceptor的职责

时间:2014-06-03 19:54:00

标签: sockets std rust

我可以很容易地在Rust中创建一个TcpListener和TcpAcceptor,但我不能完全了解他们的工作。看起来监听器并没有真正倾听,只是接受者的设置结构。

let tcp_listener = TcpListener::bind(addr);
let mut acceptor = tcp_listener.listen();
drop(acceptor);

令我困惑的一件事就是掉线。我不知道drop定义在哪里,我对标准库进行了一些搜索,但我只能将其作为特征方法。

为什么放弃接受者会停止听?难道不应该只停止接受吗?当然,你想要放弃听众。为什么甚至有一个TcpListener,因为接受器似乎做了一切?

1 个答案:

答案 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()几乎没有用,因为当相应的对象超出范围时,所有析构函数总是运行。当需要一些具有可破坏资源的复杂逻辑时偶尔会有用,但它很少发生。