Boost.Beast
阅读量
0
阅读人次
0
介绍
Beast是header-only的C++库,通过使用Boost.Asio
一致的异步模型提供底层HTTP/1,WebSocket
和网络协议的术语和算法,可作为编写通用网络库的基础库。
该库设计用于:
- 对称性:算法与角色无关;构建客户端和/或服务器。
- 易于 使用:
Boost.Asio
用户将立即了解如何使用Beast。 - 灵活性:用户可以做出重要的决定,例如缓冲区或线程管理。
- 性能:构建处理数千个或更多连接的应用程序。
- 进一步抽象的基础。组件非常适合在其上进行构建。
该库不是客户端或服务器,但是可以用来构建这些东西。提供了许多示例,包括客户端和服务器,它们可以用作编写您自己的程序的起点。
动机
Beast允许用户使用HTTP/1和WebSocket
创建自己的库,客户端和服务器。因为Beast会处理底层协议的详细信息,所以代码将更易于实现,理解和维护。 HTTP和WebSocket
协议驱动了大多数万维网。每个Web浏览器都实现这些协议以加载网页并允许客户端程序(通常以JavaScript编写)进行交互通信。C++得益于这些协议的标准化实现。
要求
重要 : 该库供熟悉Boost.Asio的程序员使用。希望使用异步接口的用户应该已经知道如何使用回调或协程创建并发网络程序。
Beast要求:
- C++11:对大多数语言功能的强大支持。
Boost
:Beast仅适用于Boost,不适用于独立的Asio
OpenSSL
:生成测试,示例以及使用TLS/Secure
套接字是必需的。
源码只有头文件。除以下情况外,通常不需要在程序的链接步骤中添加其他库以使用Beast:
- 使用通过调用
boost::asio::spawn
创建的协程时,需要将Boost.Coroutine
库添加到程序中。 - 使用
boost::asio::ssl::stream
时,您需要将OpenSSL
库添加到程序中。
请访问Boost文档以获取有关如何为特定环境系统构建和链接Boost库的说明。
快速浏览
这些完整的程序旨在使读者快速印象深刻。 它们的源代码和构建脚本位于example目录中。
简单的HTTP客户端
使用HTTP向网站发出GET请求并打印响应:
//HttpSyncClient.cpp
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = boost::asio::ip::tcp;
// Performs an HTTP GET and prints the response
int HttpSyncClient(int argc, char **argv) {
try {
auto const host = "www.baidu.com";
auto const port = "80";
auto const target = "/";
int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
// The io_context is required for all I/O
boost::asio::io_context io_context;
// These objects perform our I/O
tcp::resolver resolver(io_context);
beast::tcp_stream stream(io_context);
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
stream.connect(results);
// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
// Gracefully close the socket
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
// not_connected happens sometimes
// so don't bother reporting it.
//
if (ec && ec != beast::errc::not_connected)
throw beast::system_error{ec};
// If we get here then the connection is closed gracefully
} catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
简单的WebSocket客户端
建立WebSocket连接,发送消息并接收回复:
//WebsocketSyncClient.cpp
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
// Sends a WebSocket message and prints the response
int WebsocketSyncClient(int /*argc*/, char ** /*argv*/) {
try {
auto const host = "127.0.0.1";
auto const port = "8080";
auto const text = "Hello World!1111";
// The io_context is required for all I/O
net::io_context ioc;
// These objects perform our I/O
tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
net::connect(ws.next_layer(), results.begin(), results.end());
// Set a decorator to change the User-Agent of the handshake
ws.set_option(websocket::stream_base::decorator([](websocket::request_type &req) {
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) + " websocket-client-coro");
}));
// Perform the websocket handshake
ws.handshake(host, "/");
// Send the message
ws.write(net::buffer(std::string(text)));
// This buffer will hold the incoming message
beast::flat_buffer buffer;
// Read a message into our buffer
ws.read(buffer);
// Close the WebSocket connection
ws.close(websocket::close_code::normal);
// If we get here then the connection is closed gracefully
// The make_printable() function helps print a ConstBufferSequence
std::cout << beast::make_printable(buffer.data()) << std::endl;
} catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
安全审查(Bishop Fox)
自2005年以来,毕晓普·福克斯(Bishop Fox)为全球财富1000强,高科技初创企业和金融机构提供了安全咨询服务。 Beast与Bishop Fox合作,评估了Boost C++ Beast HTTP/S网络库的安全性。以下报告详细介绍了从2017年9月11日开始的参与过程中发现的发现。
评估团队对Beast库进行了混合应用评估。 Bishop Fox的混合应用程序评估方法利用了应用程序渗透测试的实际攻击技术,结合有针对性的源代码审查,以彻底识别应用程序安全漏洞。这些全面知识评估始于对已部署的应用程序和源代码的自动扫描。接下来,将扫描结果的分析与手动检查结合起来,以彻底识别潜在的应用程序安全漏洞。此外,该团队还对应用程序体系结构和业务逻辑进行了审查,以查找所有设计级别的问题。最后,团队对这些问题进行手动开发和审查,以验证结果。
WebSocket(Autobahn|测试套件)
Autobahn WebSockets Testsuite提供了一个全自动测试套件,可以验证WebSocket协议的客户端和服务器实现,以确保规范的一致性和实现的鲁棒性。 该测试套件将通过进行基本的WebSocket对话,广泛的协议符合性验证以及性能和限制测试来检查实现。 Autobahn|Testsuite在整个行业中使用,包含500多个测试用例。
Autobahn|Testsuite WebSocket Results
示例
客户端
这些HTTP客户端将GET请求提交到 命令行上指定的服务器,并打印结果响应。 客户端异步爬取10,000个顶级域的文档根,这可用于评估健壮性。 所有异步客户端均支持超时。
服务端
服务端(高级)
聊天服务器
网络
刷新器
为了有效地使用Beast,需要对网络有事先的了解。本节将对这些概念进行回顾,以提醒您并进一步学习。
通过建立连接,网络允许位于任何地方的程序在加入通信后交换信息。数据可以双向(全双工)可靠地通过连接传输,字节的发送顺序与接收顺序相同。这些连接以及用于表示它们的对象和类型统称为流。连接到网络的计算机或设备称为主机,建立的连接另一端的程序称为peer。
互联网是由相互连接的计算机组成的全球网络,这些计算机使用各种标准化的通信协议来交换信息。最受欢迎的协议是TCP/IP,该库仅依赖该协议。该协议负责处理底层细节,因此应用程序可以看到流(stream),这是 一种可靠的全双工连接,带有上述字节的有序集合。服务器是功能强大且始终在线的主机,位于提供数据服务的知名网络名称或网络地址上。客户端是连接到服务器以交换数据然后下线的peer端。
供应商提供了一个称为设备驱动程序的程序,该程序使网络硬件(例如以太网适配器)能够与操作系统进行通讯。这又允许运行的程序使用各种接口(例如Berkeley套接字或Windows Sockets 2(Winsock)与网络交互。
以Boost.Asio
,Asio
和Networking TS
为代表的C++网络库提供了一层抽象层,可与操作系统设备进行可移植的交互,不仅用于网络,而且用于常规输入/输出(I/O)。
缓冲区
缓冲区保存执行I/O时使用的连续字节序列。net::const_buffer
和net::mutable_buffer
类型将这些内存区域表示为类型安全的pair<指针,大小>
:
net::const_buffer cb("Hello, world!", 13);
assert(string_view(reinterpret_cast<char const*>(
cb.data()), cb.size()) == "Hello, world!");
char storage[13];
net::mutable_buffer mb(storage, sizeof(storage));
std::memcpy(mb.data(), cb.data(), mb.size());
assert(string_view(reinterpret_cast<char const*>(
mb.data()), mb.size()) == "Hello, world!");
const_buffer
和mutable_buffer
优于std::span<byte>
和span<byte const>
,因为std::span
的功能太多。 它不仅对原始指针进行类型擦除,而且将其转化为指向字节的指针。 操作系统并不关心这一点,但是如果用户想要发送和接收其他类型的数组,则不需要将其显示为支持位操作的字节数组。 自定义缓冲区类型还使实现能够提供目标功能,例如缓冲区调试,而无需更改更通用的词汇表类型。
ConstBufferSequence
和MutableBufferSequence
概念描述了双向范围,其值类型分别可转换为const_buffer
和mutable_buffer
。 这些序列允许在单个函数调用中与多个缓冲区进行事务处理,这种技术称为分散/聚集 I/O。 缓冲区和缓冲区序列是非所有者的; 副本产生浅引用,而不是基础内存的副本。 这些语句中的每一个都声明一个缓冲区序列:
net::const_buffer b1; // a ConstBufferSequence by definition
net::mutable_buffer b2; // a MutableBufferSequence by definition
std::array<net::const_buffer, 3> b3; // A ConstBufferSequence by named requirements
函数net::buffer_size
和net::buffer_copy
确定缓冲区序列中的字节总数,并将部分或全部字节分别从一个缓冲区序列传输到另一缓冲区序列。 函数buffer_size
是一个自定义点:外部命名空间中用户定义的重载是可能的,并且调用者应在没有命名空间限定的情况下调用buffer_size
。 函数net::buffer_sequence_begin
和net::buffer_sequence_end
用于获得一对遍历序列的迭代器。 Beast提供了一组缓冲区序列类型和算法,例如buffers_cat
,buffers_front
,buffers_prefix
,buffers_range
和buffers_suffix
。 本示例以字符串形式返回缓冲区序列中的字节:
template <class ConstBufferSequence>
std::string string_from_buffers (ConstBufferSequence const& buffers) {
// check that the type meets the requirements using the provided type traits
static_assert(
net::is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
// optimization: reserve all the space for the string first
std::string result;
result.reserve(beast::buffer_bytes(buffers));// beast version of net::buffer_size
// iterate over each buffer in the sequence and append it to the string
// returns an iterator to beginning of the sequence
for(auto it = net::buffer_sequence_begin(buffers);
// returns a past-the-end iterator to the sequence
it != net::buffer_sequence_end(buffers);) {
// A buffer sequence iterator's value_type is always
// convertible to net::const_buffer
net::const_buffer buffer = *it++;
// A cast is always required to out-out of type-safety
result.append(static_cast<char const*>(buffer.data()), buffer.size());
}
return result;
}
DynamicBuffer
概念定义了可调整大小的缓冲区序列接口。 当不知道内存大小时(例如,从流中读取HTTP消息时),可以用动态缓冲区来表示算法。 Beast提供了全面的动态缓冲区类型集合,例如buffers_adaptor
,flat_buffer
,multi_buffer
和static_buffer
。 以下函数使用net::buffers_iterator
将缓冲区的内容视为一个字符范围,将数据从tcp_stream
读取到动态缓冲区中,直到遇到换行符为止:
// Read a line ending in '\n' from a socket, returning
// the number of characters up to but not including the newline
template <class DynamicBuffer>
std::size_t read_line(net::ip::tcp::socket& sock, DynamicBuffer& buffer) {
// this alias keeps things readable
using range = net::buffers_iterator<
typename DynamicBuffer::const_buffers_type>;
for(;;) {
// get iterators representing the range of characters in the buffer
auto begin = range::begin(buffer.data());
auto end = range::end(buffer.data());
// search for "\n" and return if found
auto pos = std::find(begin, end, '\n');
if(pos != range::end(buffer.data()))
return std::distance(begin, end);
// Determine the number of bytes to read,
// using available capacity in the buffer first.
std::size_t bytes_to_read = std::min<std::size_t>(
std::max<std::size_t>(512, // under 512 is too little,
buffer.capacity() - buffer.size()),
std::min<std::size_t>(65536, // and over 65536 is too much.
buffer.max_size() - buffer.size()));
// Read up to bytes_to_read bytes into the dynamic buffer
buffer.commit(sock.read_some(buffer.prepare(bytes_to_read)));
}
}
同步I/O
同步I/O是通过阻塞函数调用来完成的,这些函数返回操作结果。 这些操作通常无法取消,并且没有设置超时的方法。 SyncReadStream
和SyncWriteStream
概念定义了同步流的要求:一种可移植的I/O抽象,它使用缓冲区序列表示字节并使用error_code或异常报告任何故障来传输数据。 net::basic_stream_socket
是通常用于形成TCP/IP连接的同步流。 可以满足要求的用户定义类型:
// Meets the requirements of SyncReadStream
struct sync_read_stream {
// Returns the number of bytes read upon success, otherwise throws an exception
template <class MutableBufferSequence>
std::size_t read_some(MutableBufferSequence const& buffers);
// Returns the number of bytes read successfully, sets the error code
// if a failure occurs
template <class MutableBufferSequence>
std::size_t read_some(MutableBufferSequence const& buffers, error_code& ec);
};
// Meets the requirements of SyncWriteStream
struct sync_write_stream {
// Returns the number of bytes written upon success,
// otherwise throws an exception
template <class ConstBufferSequence>
std::size_t write_some(ConstBufferSequence const& buffers);
// Returns the number of bytes written successfully, sets the error code
// if a failure occurs
template <class ConstBufferSequence>
std::size_t write_some(ConstBufferSequence const& buffers, error_code& ec);
};
同步流算法被编写为函数模板,该函数接受满足同步读取,写入或两者的命名要求的流对象。 此示例显示了一种算法,该算法可以编写文本并使用异常来指示错误:
template <class SyncWriteStream>
void hello (SyncWriteStream& stream) {
net::const_buffer cb("Hello, world!", 13);
do {
auto bytes_transferred = stream.write_some(cb); // may throw
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0);
}
可以使用错误代码代替异常来表示相同的算法:
template <class SyncWriteStream>
void hello (SyncWriteStream& stream, error_code& ec) {
net::const_buffer cb("Hello, world!", 13);
do {
auto bytes_transferred = stream.write_some(cb, ec);
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0 && ! ec);
}
异步I/O
异步操作从对initiating(启动)函数的调用开始,该调用启动该函数并立即返回到调用方。 这种出色的异步操作可以同时进行,而不会阻塞调用方。 当观察者一方建立完成后时,将在启动函数调用中提供的称为完成处理程序(completion handler)的可移动函数对象进入队列,以便与结果一起执行,该结果可能包括错误代码和其他特定信息。 在完成处理程序队列后,据说异步操作已完成。 以下代码显示了如何将某些文本异步写入套接字,并在操作完成后调用lambda:
// initiate an asynchronous write operation
net::async_write(sock, net::const_buffer("Hello, world!", 13),
[](error_code ec, std::size_t bytes_transferred) {
// this lambda is invoked when the write operation completes
if(! ec)
assert(bytes_transferred == 13);
else
std::cerr << "Error: " << ec.message() << "\n";
});
// meanwhile, the operation is outstanding and execution continues from here
每个完成处理程序也称为 continuation(延续),都具有net::get_associated_allocator
返回的关联分配器和net::get_associated_executor
返回的关联执行器。 这些关联可能被侵入地指定:
// The following is a completion handler expressed
// as a function object, with a nested associated
// allocator and a nested associated executor.
struct handler {
using allocator_type = std::allocator<char>;
allocator_type get_allocator() const noexcept;
using executor_type = boost::asio::io_context::executor_type;
executor_type get_executor() const noexcept;
void operator()(boost::beast::error_code, std::size_t);
};
或者可以通过专门化类模板net::associated_allocator
和net::associated_executor
来非侵入地指定这些关联:
namespace boost {
namespace asio {
template<class Allocator>
struct associated_allocator<handler, Allocator> {
using type = std::allocator<void>;
static
type
get(handler const& h,
Allocator const& alloc = Allocator{}) noexcept;
};
template<class Executor>
struct associated_executor<handler, Executor> {
using type = boost::asio::executor;
static
type
get(handler const& h,
Executor const& ex = Executor{}) noexcept;
};
} // boost
} // asio
当调用者想要更改完成处理程序的执行程序时,可以使用函数net::bind_executor
。
该实现使用分配器来获取执行该操作所需的任何临时存储。在调用完成处理程序之前,总是会释放临时分配。执行程序是可复制耗时成本低的对象,它提供用于调用完成处理程序的算法。除非由调用者定制,否则完成处理程序默认使用std::allocator<void>
和相应I/O对象的执行程序。
网络规定了确定处理程序运行环境的工具。每个I/O对象都引用一个ExecutionContext
,以获取用于调用完成处理程序的Executor实例。执行程序确定在何处以及如何调用完成处理程序。从net::io_context
实例获得的执行程序提供了基本保证:处理程序将仅从当前正在调用net::io_context::run
的线程中调用。
AsyncReadStream
和AsyncWriteStream
概念定义了异步流的要求:一种可移植的I/O抽象,使用缓冲区序列表示字节并使用error_code异步报告数据以报告任何故障。异步流算法被编写为模板化启动函数模板,该模板接受满足异步读取,写入或两者的命名要求的流对象。此示例显示了一种将一些文本写入异步流的算法:
template <class AsyncWriteStream, class WriteHandler>
void async_hello (AsyncWriteStream& stream, WriteHandler&& handler) {
net::async_write (stream,
net::buffer("Hello, world!", 13),
std::forward<WriteHandler>(handler));
}
并发
I/O对象(例如套接字和流)不是线程安全的。尽管可能有多个未完成的操作(例如,同时异步读取和异步写入),但流对象本身一 次只能从一个线程访问。这意味着不得同时调用成员函数,例如移动构造函数,析构函数或启动函数。通常,这是通过同步原语(例如互斥锁)来完成的,但是并发网络程序需要一种更好的方式来访问共享资源,因为获取互斥锁的所有权可能会阻止线程执行无人参与的工作。为了提高效率,网络采用了一种在不需要显式锁定的情况下使用线程的模型,因为该模型要求所有对I/O对象的访问都必须在一个strand
中执行。
通用模型
因为完成处理程序导致控制流的反转,所以有时需要其他附加延续的方法。网络为异步操作提供了通用模型,它提供了一种可自定义的方式来转换启动函数的签名,以使用其他类型的对象和方法来代替完成处理程序回调。例如,使用std::future接收异步传输的字符串,从而接收到传输的字节数,如下所示:
std::future<std::size_t> f = net::async_write(sock,
net::const_buffer("Hello, world!", 13), net::use_future);
通过传递变量net::use_future
(类型为net::use_future_t<>
)来代替完成处理程序,可以启用此功能。 相同的async_write
函数重载可以与使用asio :: spawn
启动的协程一起使用:
asio::spawn(
[&sock](net::yield_context yield)
{
std::size_t bytes_transferred = net::async_write(sock,
net::const_buffer("Hello, world!", 13), yield);
(void)bytes_transferred;
});
在这两种情况下,都使用具有特定类型的对象代替CompletionHandler
,并且将初始化函数的返回值从void转换为std::future<std::size_t>
或std::size_t
。 在此上下文中使用时,该处理程序有时称为CompletionToken
。 返回类型转换由启动函数签名中的定制点支持。 这是net::async_write
的签名:
return net::async_initiate<
CompletionToken,
void(error_code, std::size_t)>(
run_async_write{}, // The "initiation" object.
token, // Token must come before other arguments.
&stream, // Additional captured arguments are
buffers); // forwarded to the initiation object.
这个经过转换的内部处理程序负责完成步骤,该步骤将操作结果传递给调用方。 例如,当使用net::use_future
时,内部处理程序将通过在启动函数返回的promise对象上调用std::promise::set_value
来传递结果。
使用Networking
大多数库流算法需要一个tcp::socket
,net::ssl::stream
或其他已经与远程对等方建立通信的Stream对象。 提供此示例是为了提醒您如何使用套接字:
// The resolver is used to look up IP addresses and port numbers from a domain and service name pair
tcp::resolver r{ioc};
// A socket represents the local end of a connection between two peers
tcp::socket stream{ioc};
// Establish a connection before sending and receiving data
net::connect(stream, r.resolve("www.example.com", "http"));
// At this point `stream` is a connected to a remote
// host and may be used to perform stream operations.
在本文档中,具有以下名称的标识符具有特殊含义:
-
ioc
net::io_context
类型的变量,它在一个单独的线程上运行,并在其上构造了net::executor_work_guard
对象。 -
sock
类型为
tcp::socket
的变量,该变量已经连接到远程主机。 -
ssl_sock
net::ssl::stream<tcp::socket>
类型的变量,该变量已经连接并且已与远程主机握手。 -
ws
类型为
websocket::stream<tcp::socket>
的变量,该变量已与远程主机连接。
流
Stream是一种通信通道,在该通道中,数据作为字节的有序序列被可靠地传输。 流可以是同步的也可以是异步的,并且可以允许读,写或两者兼而有之。 请注意,特定类型可以为多个概念建模。 例如,网络类型tcp::socket
和net::ssl::stream
支持SyncStream和AsyncStream
。 使用以下概念将Beast中的所有流算法声明为模板函数:
-
SyncReadStream
支持面向缓冲区的阻塞读取。
-
SyncWriteStream
支持面向缓冲区的阻塞写入。
-
SyncStream
流支持面向缓冲区的阻塞读取和写入。
-
AsyncReadStream
支持面向缓冲区的异步读取。
-
AsyncWriteStream
支持面向缓冲区的异步写入。
-
AsyncStream
流支持面向缓冲区的异步读写。
这些模板元函数检查给定类型是否满足各种流概念的要求以及一些其他有用的实用程序。 该库在内部使用这些类型检查,还提供它们作为公共接口,因此用户可以使用相同的技术来扩展自己的代码。 这些类型检查的使用有助于在编译期间提供更简洁的错误:
-
executor_type
get_executor返回的对象类型的别名。
-
has_get_executor
确定get_executor成员函数是否存在。
-
is_async_read_stream
确定类型是否满足AsyncReadStream的要求。
-
is_async_stream
确定类型是否同时满足AsyncReadStream和AsyncWriteStream的要求。
-
is_async_write_stream
确定类型是否满足AsyncWriteStream的要求。
-
is_sync_read_stream
确定类型是否符合SyncReadStream的要求。
-
is_sync_stream
确定类型是否同时满足SyncReadStream和SyncWriteStream的要求。
-
is_sync_write_stream
确定类型是否符合SyncWriteStream的要求。
对函数或类模板类型使用带有static_assert的类型检查,将为用户提供有用的错误消息并防止未定义的行为。 此示例说明了写入同步流的模板函数如何检查其参数:
template<class SyncWriteStream>
void write_string(SyncWriteStream& stream, string_view s) {
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream type requirements not met");
net::write(stream, net::const_buffer(s.data(), s.size()));
}
超时
限速
分层流
计数流
缓冲区类型
为了便于处理Boost.Asio中引入的ConstBufferSequence和MutableBufferSequence概念的实例,Beast将这些序列视为一种特殊的range
。 提供了以下算法和包装器,可以使用惰性求值计算有效地转换这些range
。 转换中不使用任何内存分配。 相反,它们在现有未修改的内存缓冲区上创建轻量级迭代器。 缓冲区的控制权由调用方保留; 所有权不转移。
-
buffer_bytes
这是
net::buffer_size
的更可靠版本,它更易于使用,并且还适用于可转换为net::const_buffer
或net::mutable_buffer
的类型。 -
buffers_cat
此函数返回一个新的缓冲区序列,该缓冲区序列在进行迭代时会遍历如果所有输入缓冲区序列都串联在一起将形成的序列。使用此例程,可以将对流的write_some函数的多个调用组合为一个,从而消除了昂贵的系统调用。
-
buffers_cat_view
此类表示通过串联两个或多个缓冲区序列形成的缓冲区序列。这是buffers_cat返回的对象的类型。
-
buffers_front
此函数返回缓冲区序列中的第一个缓冲区,如果缓冲区序列中没有元素,则返回大小为零的缓冲区。
-
buffers_prefix
该函数返回一个新的缓冲区或缓冲区序列,它代表原始缓冲区的前缀。
-
buffers_prefix_view
此类表示由现有缓冲区序列的前缀形成的缓冲区序列。这是buffers_prefix返回的缓冲区的类型。
-
buffers_range buffers_range_ref
此函数返回表示传递的缓冲区序列的可迭代范围。迭代范围时获得的值将始终是常量缓冲区,除非基础缓冲区序列是可变的,在这种情况下,迭代时获得的值将是可变缓冲区。在缓冲区序列上编写范围语句时,其目的是为了提供符号方便。
函数buffers_range维护缓冲区序列的副本,而buffers_range_ref维护引用(在这种情况下,调用者必须确保所引用的缓冲区序列的生存期延长,直到范围对象被破坏为止)。
-
buffers_suffix
此类包装现有缓冲区序列的基础内存,并提供原始序列的后缀。后缀的长度可以逐渐缩短。这使调用者可以按顺序递增缓冲区序列。
-
buffers_to_string
此函数将缓冲区序列转换为std :: string。它可以用于诊断目的和测试。
Boost.Asio中引入的DynamicBuffer概念对支持自己可调整范围的缓冲区序列进行建模。 Beast提供了动态缓冲区概念的这套附加实 现:
-
buffers_adaptor
该包装器将任何MutableBufferSequence调整为DynamicBuffer,其输入和输出区域的总大小的上限等于基础可变缓冲区序列的大小。该实现不执行堆分配。
-
flat_buffer basic_flat_buffer
确保输入和输出区域是长度为1的缓冲区序列。在构造时,可以设置输入和输出区域的总大小的可选上限。基本容器是AllocatorAwareContainer。
-
multi_buffer basic_multi_buffer
使用一系列大小不同的一个或多个字符数组。其他字符数组对象将附加到序列中,以适应字符序列大小的变化。基本容器是AllocatorAwareContainer。
-
flat_static_buffer flat_static_buffer_base
确保输入和输出区域是长度为1的缓冲区序列。提供动态缓冲区的功能,但要限制由constexpr模板参数定义的输入和输出区域的总大小的上限。序列的存储保存在类中;该实现不执行堆分配。
-
static_buffer static_buffer_base
提供循环动态缓冲区的功能。取决于由constexpr模板参数定义的输入和输出区域的总大小的上限。该实现永远不会在缓冲区操作期间移动内存。序列的存储保存在类中;该实现不执行堆分配。
这两个函数有助于缓冲区与标准输出流的互操作性。
-
make_printable
该函数包装了ConstBufferSequence,因此可以与
operator <<
和std::ostream
一起使用。 -
ostream
-
此函数返回一个包装动态缓冲区的
std::ostream
。 使用operator<<
发送到流的字符存储在动态缓冲区中。
提供这些类型特征以方便编写在缓冲区上运行的编译时元函数:
buffers_iterator_type
此元函数用于确定特定缓冲区序列使用的迭代器的类型。
buffers_type