Asio:是否有可自动调整大小的缓冲区来接收输入?
我事先并不知道要收到的尺寸,所以我将这个数量发送到标题中。
我查看http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp使用标题的示例,但此示例假定指定了最大体型。
查看asio :: buffer类,我必须提供一些底层缓冲区,因此不灵活。相反,我看了一下asio :: streambuf类,但是如下所示使用它会产生分段/内存错误。
我尝试给出最大大小以只读取HEADER_LEN
个字节,即标题。
这种方法有误吗?
void do_recv_header()
{
asio::streambuf buf(HEADER_LEN);
asio::async_read(*g_selected_conn, buf, [this, &buf](const system::error_code& ec, std::size_t bytes_transferred)
{
if (ec != 0) {
std::cout << "async_read() error: " << ec.message() << " (" << ec.value() << ") " << std::endl;
remove_closed_conn(g_selected_conn);
SetEvent(g_wait_event);
}
else {
std::istream is(&buf);
int body_len;
is >> body_len;
std::cout << body_len << std::endl;
do_recv_body(body_len);
}
});
}
答案 0 :(得分:6)
boost::asio::streambuf是一个可自动调整大小的缓冲类。这种缓冲区类型通常在启动读取操作时使用,该读取操作的完成取决于数据的内容,而不一定是数据的大小。例如,可以使用boost::asio::read_until()
来读取换行符,而不知道或指定可读取的数据量。
如果应用程序协议具有包含主体长度的固定大小标头,并且标头后跟可变长度主体,请考虑使用缓冲区类型,例如std::vector<>
。这将提供与boost::asio::streambuf
相同的灵活性,同时简化部分簿记:
std::vector<char> buffer;
// Read header.
buffer.resize(protocol::header_size);
boost::asio::read(socket, boost::asio::buffer(buffer));
// Extract body size from header, resize buffer, then read
// body.
auto body_size = parse_header(buffer);
buffer.resize(body_size);
boost::asio::read(socket, boost::asio::buffer(buffer));
process_body(buffer);
不调整vector
的大小,表明在读取操作中将读取多少数据。使用streambuf
时,必须使用以下操作直接管理输入和输出序列:
boost::asio::streambuf streambuf;
// Read header into the streambuf's output sequence.
auto bytes_transferred = boost::asio::read(socket,
streambuf.prepare(protocol::header_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);
// Extract body size from header. This would likely
// consume all of the streambuf's input sequence.
auto body_size = parse_header(streambuf);
// Clear the input sequence.
streambuf.consume(streambuf.size());
// Ready body into the streambuf's output sequence.
bytes_transferred = boost::asio::read(socket,
streambuf.prepare(body_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);
// Extract all of stream into the body.
process_body(streambuf);
这是一个完整的示例demonstrating这种方法:
#include <array> // std::array
#include <functional> // std::bind
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
#include <boost/asio.hpp>
// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}
// The application protocol will consists of a fixed-size header
// containing a std::size_t with the length of the following
// variable length body. To keep it simple, some details
// are ommitted, such as endian handling.
namespace protocol {
enum
{
header_size = sizeof(std::size_t)
};
} // namespace protocol
std::vector<char> build_header(const std::string& body)
{
std::vector<char> buffer(protocol::header_size);
auto body_size = body.size();
std::memcpy(&buffer[0], &body_size, sizeof body_size);
return buffer;
}
std::size_t parse_header(const std::vector<char>& buffer)
{
return *reinterpret_cast<const std::size_t*>(&buffer[0]);
}
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket1(io_service);
tcp::socket socket2(io_service);
// Connect the sockets.
acceptor.async_accept(socket1, std::bind(&noop));
socket2.async_connect(acceptor.local_endpoint(), std::bind(&noop));
io_service.run();
io_service.reset();
// Write a message from socket1 to socket2.
std::string test_message = "this is a test message";
{
auto header = build_header(test_message);
// Gather header and body into a single buffer.
std::array<boost::asio::const_buffer, 2> buffers = {{
boost::asio::buffer(header),
boost::asio::buffer(test_message)
}};
// Write header and body to socket.
boost::asio::write(socket1, buffers);
}
// Read from socket2.
{
// Use a vector to allow for re-sizing based on the
// amount of data needing to be read. This also reduces
// on the amount of reallocations if the vector is reused.
std::vector<char> buffer;
// Read header.
buffer.resize(protocol::header_size);
boost::asio::read(socket2, boost::asio::buffer(buffer));
// Extract body size from header, resize buffer, then read
// body.
auto body_size = parse_header(buffer);
buffer.resize(body_size);
boost::asio::read(socket2, boost::asio::buffer(buffer));
// Verify body was read.
assert(std::equal(begin(buffer), end(buffer),
begin(test_message)));
std::cout << "received: \n"
" header: " << body_size << "\n"
" body: ";
std::cout.write(&buffer[0], buffer.size());
std::cout << std::endl;
}
}
输出:
received:
header: 22
body: this is a test message