You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Boost.Asio的ssl::stream正确添加缓冲?

问题背景与需求

我们代码库中有直接使用OpenSSL的C++网络代码,为提升可维护性,计划用Boost.Asio替代。该代码用于服务器与客户端间的双向认证TLS连接,支持收发字节数组。基准测试显示Asio版本运行时间是OpenSSL版本的近5倍,原因是OpenSSL版本用了网络通信缓冲,而Asio版本没有。由于应用频繁收发大量小消息,缓冲对性能至关重要(API包含flush函数可按需刷新发送缓冲区)。

手动实现了用std::vector<byte>管理缓冲的逻辑,让应用先写入缓冲,仅调用flush时才发送网络数据,性能有所提升,但仍比OpenSSL版本慢约30%。注意到Boost.Asio的buffered_stream类似乎正是所需,但不清楚底层流为boost::asio::ssl::stream类型时如何正确构造。另外,如果这种方法不可行,有没有更好的Asio缓冲I/O方式能达到和OpenSSL版本相近的性能?

尝试的代码
using namespace boost::asio;
using ssl_socket_t = ssl::stream<ip::tcp::socket>;

// 此函数(在别处定义)返回配置正确的客户端ssl::context对象
ssl::context tlsClientContext(std::string_view certificate_filename,
                              std::string_view private_key_filename,
                              std::string_view trust_store_filename);


// 尝试实现如下API:
buffered_stream<ssl_socket_t> connect(std::string_view hostname,
                                      std::uint16_t port,
                                      std::string_view certificate_filename,
                                      std::string_view private_key_filename,
                                      std::string_view trust_store_filename) {
  auto ssl_ctx = tlsClientContext(certificate_filename, private_key_filename, trust_store_filename);
  auto ssl_socket = ssl_socket_t(system_executor(), ssl_ctx);
  auto resolver = ip::tcp::resolver(ssl_socket.get_executor());
  connect(ssl_socket.lowest_layer(), resolver.resolve(hostname, std::to_string(port)));
  ssl_socket.handshake(ssl_socket_t::client);
  return buffered_stream<ssl_socket_t>(std::move(ssl_socket));
}

void read(buffered_stream<ssl_socket_t>& stream, std::byte* data, std::size_t num_bytes) {
  boost::asio::read(stream, buffer(data, num_bytes));
}

void write(buffered_stream<ssl_socket_t>& stream, const std::byte* data, std::size_t num_bytes) {
  boost::asio::write(stream, buffer(data, num_bytes));
}

void flush(buffered_stream<ssl_socket_t>& stream) {
  stream.flush();
}
编译问题

上述connect函数无法编译:buffered_stream构造函数要求第一个参数为左值;若移除std::move,又因ssl::stream不可拷贝而编译失败。原本期望buffered_stream支持传入待缓冲流的右值引用,但不知道如何实现。

解决方案

1. 正确构造buffered_stream的方法

Boost.Asio的buffered_stream在C++11及以后版本支持移动构造,可通过以下方式解决构造问题:

  • 使用make_buffered_stream辅助函数:它天然支持接收右值,代码调整如下:
    return make_buffered_stream(std::move(ssl_socket));
    
  • 显式移动构造:确保Boost版本在1.70+(该版本后buffered_stream完善了右值构造支持),直接返回移动构造的对象:
    return buffered_stream<ssl_socket_t>(std::move(ssl_socket));
    

2. 优化性能的其他方案

如果buffered_stream仍无法达到预期性能,可尝试以下方案:

  • 自定义缓冲区大小:默认缓冲区大小可能不匹配小消息场景,构造时指定合适的大小(比如8KB或16KB,根据实际业务调整):
    return make_buffered_stream(std::move(ssl_socket), 8192);
    
  • 手动实现灵活缓冲逻辑:自己维护动态缓冲区,完全控制消息合并与发送时机,示例代码如下:
    class BufferedSslStream {
    public:
        BufferedSslStream(ssl_socket_t socket) : socket_(std::move(socket)) {}
    
        void write(const std::byte* data, std::size_t num_bytes) {
            buffer_.insert(buffer_.end(), data, data + num_bytes);
        }
    
        void flush() {
            if (!buffer_.empty()) {
                boost::asio::write(socket_, boost::asio::buffer(buffer_));
                buffer_.clear();
            }
        }
    
        void read(std::byte* data, std::size_t num_bytes) {
            boost::asio::read(socket_, boost::asio::buffer(data, num_bytes));
        }
    
    private:
        ssl_socket_t socket_;
        std::vector<std::byte> buffer_;
    };
    
  • 启用OpenSSL内部缓冲:利用OpenSSL原生API开启缓冲模式,减少系统调用次数:
    SSL* ssl_handle = ssl_socket.native_handle();
    SSL_set_mode(ssl_handle, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
    

内容的提问来源于stack exchange,提问作者Dave Edwards

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 01:42:53