【问题标题】:Do I need to move away from Tokio as I cannot split streams in TLS connections?我是否需要离开 Tokio,因为我无法在 TLS 连接中拆分流?
【发布时间】:2020-04-08 08:43:03
【问题描述】:

我使用 Tokio 创建普通的 TCP 套接字,调用 tokio::io::split() 并将读/写部分交给单独的线程。他们使用带有await 的异步套接字读/写API 来完成IO。我们的数据流在输入/输出方向上是相当隔离的,所以这个模型在我们的例子中运行良好。到目前为止,一切都很好。

现在正在考虑在顶部添加 TLS 支持。由于各种原因,一些 TLS 库不允许拆分流:

  • tokio-rustls(使用 rustls 实现)允许拆分,但相对较新

  • 我更喜欢使用 tokio-openssl(用 openssl 实现),它已经存在了很长时间,但 openssl does not support it。这可能是因为 TLS 重新协商等事件需要传播到读/写部分(rustls 管理它,因为它是原生 Rust 实现)。

所以需要同一个线程进行读/写。这意味着套接字现在需要变为非阻塞:不能等待数据进来,因为数据可能需要立即发送出去(反之亦然)。

如果我理解正确,Tokio/await 范式对非阻塞套接字没有意义。我的理解正确吗?

也欢迎在这种情况下提出任何其他想法。我希望在付出了所有努力之后,我们不需要放弃 Tokio。

【问题讨论】:

  • socket 现在需要变成非阻塞的——它可能已经非阻塞的了。 Tokio/await 范式不适用于非阻塞套接字——这几乎是整个async 生态系统存在的全部原因:处理非阻塞 IO 更容易推理。你能分享一下你为什么相信相反的观点吗?
  • 这里的非阻塞是指socket状态:O_NONBLOCK标志。通常的做法是:socket处于阻塞模式,一个read().await等待数据进来。async系统确保线程在等待时被放弃。但是大多数 r/w 方法都采用&mut self,所以当这个等待处于挂起状态时,不能进行写入。这就是为什么我认为套接字本身需要处于非阻塞模式。如果数据存在而没有实际(异步)阻塞,则这允许“轮询”套接字。希望我回答了你的问题
  • 指的是套接字状态:O_NONBLOCK 标志——当然,tokio::net::TcpStream 调用 mio::net::TcpStream,上面写着:“非阻塞 TCP 流”并且它 @ 987654324@.
  • 某些 TLS 库由于各种原因不允许拆分流 - 您需要edit 您的问题更具体;理想情况下提供minimal reproducible exampletokio::io::split 只要求底层类型实现 AsyncRead + AsyncWrite。我为“tokio tls”找到的第一个搜索结果实现了这两个特征。
  • 关于链接线程,它明确表示您可以通过将其放入互斥锁中进行拆分。这基本上就是 tokio::io::split 所做的。

标签: asynchronous rust async-await rust-tokio


【解决方案1】:

确实,启用了 async/await 的 TLS 库(例如 tokio-tls)要求提供的流没有被拆分,但是一旦您将流包装在 TLS 层中,您就可以拆分 那个使用 tokio::io::split 包装的流。

以这种方式使用流可以正确处理有关阻塞和非阻塞 IO 的所有细节。您无需手动配置 O_NONBLOCK 等标志,因为 Tokio 的 TcpStreamtokio-tlsTlsStream 在幕后为您处理这些细节。

使用提供阻塞套接字的库自然不会与 Tokio 兼容。这并不是什么新鲜事,与您不能在 Tokio 中使用 std::net::TcpStream 的原因相同,因为它是一个阻塞流。 Tokio 为这些目的提供了备用流类型以避免这些问题。

如果您想使用未启用 async/await 的 ssl crate,您可以在内存缓冲区上执行加密,并使用 Tokio 的 TcpStream 手动写入加密数据。启用 async/await 的 ssl 库都以这种方式运行。

【讨论】:

    猜你喜欢
    • 2020-05-28
    • 2010-12-17
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 2011-08-21
    • 2018-03-16
    相关资源
    最近更新 更多