Buffers
Learn how Asio represents memory for I/O operations.
The buffer() Function
The buffer() function creates a buffer view from various types:
#include <boost/asio.hpp>
namespace asio = boost::asio;
void buffer_examples()
{
// From a char array
char arr[1024];
auto b1 = asio::buffer(arr); // Entire array
auto b2 = asio::buffer(arr, 512); // First 512 bytes
// From std::array
std::array<char, 1024> std_arr;
auto b3 = asio::buffer(std_arr);
// From std::vector
std::vector<char> vec(1024);
auto b4 = asio::buffer(vec);
// From std::string (read-only)
std::string str = "Hello";
auto b5 = asio::buffer(str); // const_buffer
// From std::string_view (read-only)
auto b6 = asio::buffer(std::string_view("Hello"));
}
Mutable vs Const Buffers
Asio distinguishes between buffers you can write to and buffers you can only read:
| Type | Use |
|---|---|
|
For receiving data ( |
|
For sending data ( |
buffer() returns the appropriate type based on the source:
char arr[1024];
auto mb = asio::buffer(arr); // mutable_buffer
const char* data = "Hello";
auto cb = asio::buffer(data, 5); // const_buffer
std::string str = "Hello";
auto cb2 = asio::buffer(str); // const_buffer (string is treated as const)
Buffer Sequences
Many operations accept a sequence of buffers (scatter/gather I/O):
asio::awaitable<void> scatter_gather_example(asio::ip::tcp::socket& socket)
{
// Gather write: send multiple buffers in one operation
std::string header = "HTTP/1.1 200 OK\r\n\r\n";
std::string body = "Hello, World!";
std::array<asio::const_buffer, 2> buffers = {
asio::buffer(header),
asio::buffer(body)
};
co_await asio::async_write(socket, buffers, asio::use_awaitable);
// Scatter read: receive into multiple buffers
char header_buf[100];
char body_buf[1000];
std::array<asio::mutable_buffer, 2> recv_buffers = {
asio::buffer(header_buf),
asio::buffer(body_buf)
};
co_await socket.async_read_some(recv_buffers, asio::use_awaitable);
}
Dynamic Buffers
For growing buffers (useful with async_read_until):
asio::awaitable<void> dynamic_buffer_example(asio::ip::tcp::socket& socket)
{
// Using std::string as a dynamic buffer
std::string data;
std::size_t n = co_await asio::async_read_until(
socket,
asio::dynamic_buffer(data),
'\n',
asio::use_awaitable);
std::cout << "Received line: " << data.substr(0, n);
// Or with std::vector
std::vector<char> vec_data;
co_await asio::async_read_until(
socket,
asio::dynamic_buffer(vec_data),
"\r\n\r\n",
asio::use_awaitable);
}
streambuf
asio::streambuf provides iostream-compatible buffering:
asio::awaitable<void> streambuf_example(asio::ip::tcp::socket& socket)
{
asio::streambuf buf;
// Read until newline
co_await asio::async_read_until(socket, buf, '\n', asio::use_awaitable);
// Extract the data
std::istream is(&buf);
std::string line;
std::getline(is, line);
std::cout << "Line: " << line << "\n";
}
Buffer Size and Consumption
Get buffer size:
auto buf = asio::buffer(data, 100);
std::size_t size = buf.size(); // 100
After a partial read, you often need to work with a subset:
char data[1024];
auto buf = asio::buffer(data);
// Read some data
std::size_t n = co_await socket.async_read_some(buf, asio::use_awaitable);
// Create a buffer for just the received data
auto received = asio::buffer(data, n);
// Or advance past consumed data
buf += n; // Now points to data[n] with size 1024-n
Buffer Lifetime
Critical: The buffer must remain valid until the async operation completes.
// WRONG: string destroyed before operation completes
asio::awaitable<void> bad_example(asio::ip::tcp::socket& socket)
{
{
std::string msg = "Hello";
socket.async_write_some(asio::buffer(msg), ...); // BAD!
} // msg destroyed here, but write may still be in progress
}
// CORRECT: keep the buffer alive
asio::awaitable<void> good_example(asio::ip::tcp::socket& socket)
{
std::string msg = "Hello";
co_await asio::async_write(socket, asio::buffer(msg), asio::use_awaitable);
// msg still valid here, operation is complete
}
For operations that outlive the current scope, store the buffer in a member
variable or use std::shared_ptr.
Buffer Debugging
Enable buffer debugging to catch lifetime issues:
// Define before including Asio headers
#define BOOST_ASIO_ENABLE_BUFFER_DEBUGGING
#include <boost/asio.hpp>
This adds runtime checks that detect use of invalidated buffers.
Common Patterns
Fixed-size read buffer
char buf[8192];
for (;;)
{
std::size_t n = co_await socket.async_read_some(
asio::buffer(buf), asio::use_awaitable);
// Process buf[0..n)
}
Common Mistakes
Buffer too small — Reading into a small buffer silently truncates data. Size your buffers appropriately.
Buffer goes out of scope — The most common bug. Always ensure buffers outlive the async operation.
Modifying buffer during operation — Don’t modify the buffer while an async operation is using it.
Next Steps
-
Reading and Writing — Use buffers with I/O
-
Error Handling — Handle partial reads/writes