Rust中TcpListener和TcpAcceptor的职责

Jer*_*oen 1 sockets std rust

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

let tcp_listener = TcpListener::bind(addr);
let mut acceptor = tcp_listener.listen();
drop(acceptor);
Run Code Online (Sandbox Code Playgroud)

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

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

Vla*_*eev 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()系统调用并阻止当前任务,直到某个客户端建立一个连接,导致TcpStream- 一个表示客户端套接字的对象.

调用其析构函数时,将释放与所有套接字关联的所有OS资源.Rust中的析构函数可以与任何自定义结构相关联.要在结构中添加析构函数,必须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
}
Run Code Online (Sandbox Code Playgroud)

这个程序打印Dropping Test: value is 10.

对于Drop在其上实现特征的结构而言,重要的是它们不是隐式可复制的.这意味着当这些结构的实例传递给函数参数或分配给不同的变量时,它们会从原始位置移出,也就是说,您不能再通过它们的原始变量使用它们.例如(使用Test上面的定义):

let test = Test { value: 10 };
let test2 = test;
println!("{}", test.value);  // error: use of moved value: `test`
Run Code Online (Sandbox Code Playgroud)

drop()困惑你的功能在core::mem模块中定义; 这是它的文档.它的实现非常简单; 它看起来像这样:

fn drop<T>(x: T) { }
Run Code Online (Sandbox Code Playgroud)

它什么都不做!但是因为它通过值接受它的参数,所以该参数被移入drop()调用中.然后所述参数立即超出范围,因为函数返回.如果T有一个与之关联的析构函数,它将在这里运行.因此,drop()本质上是一个"黑洞":你所投入的一切都被摧毁(除非它是隐式可复制的,即).该语言的非常好的属性是这样的函数可以用简单的Rust编写,没有任何魔法.

事实上,drop()几乎没有用,因为当相应的对象超出范围时,所有析构函数总是运行.当需要一些具有可破坏资源的复杂逻辑时偶尔会有用,但它很少发生.