【问题标题】:OpenSSL SSL_write from multiple buffers / SSL_writev来自多个缓冲区的 OpenSSL SSL_write / SSL_writev
【发布时间】:2016-11-07 00:11:02
【问题描述】:

我编写了一个将 OpenSSL 用于 SSL/TLS 的网络服务器。服务器发送和接收大块数据,并在两者之间执行各种转换。出于性能原因,转换主要使用向量信息(参见 POSIX 的 iovec)来完成,以避免昂贵的内存移动(memcpy() 等)。当数据准备好发送时,我使用 writev() POSIX 函数,该函数使用这些向量从内存中收集数据,并通常将其作为一个网络数据包发送。

现在使用 OpenSSL,这并不完全可能,因为据我所知,OpenSSL 仅提供 SSL_write() 函数。这意味着我必须为要发送的每个向量条目调用此函数。不幸的是,这会导致每个矢量数据块都在其自己的 SSL 帧中传输,从而引入了不必要的和不必要的网络开销。

我的问题是:SSL_writev() 是否等同于 writev()?或者一般来说,有没有一种技术可以告诉 OpenSSL 将 SSL_write() 数据存储到一个 SSL 应用程序记录(类型 22)中而不发送它(当然还有某种 flush() 函数)?

编辑:如下所述,一种可行的方法是在最终的单个 SSL_write() 调用之前将矢量数据整合到一个大块中。然而,有 2 个副本的连接开销(第一个在合并期间,第二个在 SSL_write() 执行 AES 加密时)。 理论上的 SSL_writev() 调用不会引入这种开销。

【问题讨论】:

  • 我认为你需要一个“上拉”功能。即,将多个缓冲区合并为一个。
  • 这正是我今天所做的。但它是相当昂贵的 b/c 它在内存中移动大量数据。
  • @ateska 但是它在内存中移动大量数据是相当昂贵的 b/c。 在 Linux 上,writev()actually implemented 作为一个包装器在分配临时缓冲区的write() 周围,将writev() 缓冲区复制到临时缓冲区中,然后调用write()。如果您在 Linux 上运行并且 writev() 在没有 SSL 的情况下为您工作,只需编写您自己的 SSL_writev() 包装器。
  • @AndrewHenle - writev() 实现的好处。我希望它使用分散/收集内核功能。
  • @jww - 同意,高效的复制很重要。然而,这仍然意味着 SSL 版本将执行 2 个副本:第一个是“上拉”,第二个是 SSL 写入期间的 AES(或类似)加密。 SSE4/AVX 和 AES-NI 确实可以分别“加速”两者。我正在寻找将其合并为 1 个副本,这是 SSL_writev() 背后的原始想法。

标签: c openssl posix


【解决方案1】:

您可以使用BIO_f_buffer() 来实现此目的。将您的网络层 BIO 包装在 BIO_f_buffer() 过滤器 BIO 中,并将其设置为 SSL 对象的写入 BIO。这将导致所有写出的数据都保留在缓冲区中,直到您对其进行刷新。

【讨论】:

  • 顺便说一句,您可能希望等到握手完成后再执行此操作 - 否则您将不得不为每个交换的握手消息手动发出“刷新”命令。
  • 同意,但它仍然会为每次调用 SSL_write 创建一个 SSL 应用程序记录(类型 23)。这意味着我想避免不必要的网络开销。
  • 是的,它会的——尽管从你的描述中听起来你主要关心的是避免多个网络数据包。这与 TLS 记录不同。多个记录可以包含在单个 TCP 数据包中,也可以拆分为多个记录。通过以我建议的方式进行缓冲和刷新,这为网络层提供了以最有效的方式通过网络传输数据的最佳机会。然后,开销被限制为额外的记录头字节(5 个字节)加上 MAC 大小(取决于密码套件)。如果您的目标是减少记录数,那么...
  • ...您可以反过来做同样的事情,即在 SSL BIO 前面放置一个 BIO_f_buffer() 并在准备好时将其刷新到 SSL 层。
  • 我对这个答案的一般问题是网络开销或内存复制操作都存在不必要的牺牲。我的目标是避免两者(因为我已经在这个级别上有一个可行的解决方案)。从架构的角度来看,它非常简单:...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 2021-01-19
相关资源
最近更新 更多