流、短读和短写(Streams, Short Reads and Short Writes)#
Boost.Asio中許多 I/0对象都是面向流的: - 没有消息边界。数据通过连续字节序列传输 - 读取或写入操做传输的数据可能少于请求的字节。这就叫做 短读(short read) or 短写(short write)。
提供面向流的I/O模型满足一个或多个如下要求: - SyncReadStream,使用read_some()的成员函数执行。 - AsyncReadStream,使用async_read_some()的成员函数执行。 - SyncWriteStream,使用write_some()的成员函数执行。 - AsyncWriteStream,使用async_write_some()的成员函数执行。
面向流的I/O对象有:ip::tcp::socket, ssl::stream<>, posix::stream_descriptor, windows::stream_handle 等
当短读(short read) or 短写(short write)出现时,程序必须重新启动操作,直到传输了所需的字数。Boost.Asio 提供自动执行此操作的常规函数:read()、async_read()、write()和async_write()。
Why EOF is an Error#
- stream结束符可以导致 read, async_read, read_until or async_read_until函数终止。例如,由于 EOF,读取 N 个字节可能会提前完成。
- EOF 错误可用于区分流的末尾和大小为 0 的成功读取。
Reactor式操作(Reactor-Style Operations)#
有时需要集成使用自定义I/0操作的第三方库。Boost.Asio
包括同步和异步操作,这些操作可用于等待socket准备好读取、准备写入或出现挂起的错误情况。下面是一个非阻塞读的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14ip::tcp::socket socket(my_io_context);
...
socket.non_blocking(true);
...
socket.async_wait(ip::tcp::socket::wait_read, read_handler);
...
void read_handler(boost::system::error_code ec)
{
if (!ec)
{
std::vector<char> buf(socket.available());
socket.read_some(buffer(buf));
}
}
基于行的操作(Line-Based Operations)#
许多常用的 internet
协议是基于行的,它们具有由字符序列“”分隔的元素,如
HTTP、SMTP 和 FTP。为了更容易地实现基于行的协议,Boost.Asio 提供了函数
read_until()和async_read_until()。 如下是在一个HTTP服务器使用
async_read_until() 的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28class http_connection
{
...
void start()
{
boost::asio::async_read_until(socket_, data_, "\r\n",
boost::bind(&http_connection::handle_request_line, this, _1));
}
void handle_request_line(boost::system::error_code ec)
{
if (!ec)
{
std::string method, uri, version;
char sp1, sp2, cr, lf;
std::istream is(&data_);
is.unsetf(std::ios_base::skipws);
is >> method >> sp1 >> uri >> sp2 >> version >> cr >> lf;
...
}
}
...
boost::asio::ip::tcp::socket socket_;
boost::asio::streambuf data_;
};
分隔符可以指定为单个char、std::string或boost::regex。read_until()和async_read_until()函数还接受称为match
condition的用户自定义函数对象的重载。例如,将数据读取到streambuf,直到遇到空白:
1
2
3
4
5
6
7
8
9
10
11
12
13
14typedef boost::asio::buffers_iterator<
boost::asio::streambuf::const_buffers_type> iterator;
std::pair<iterator, bool> match_whitespace(iterator begin, iterator end)
{
iterator i = begin;
while (i != end)
if (std::isspace(*i++))
return std::make_pair(i, true);
return std::make_pair(i, false);
}
...
boost::asio::streambuf b;
boost::asio::read_until(s, b, match_whitespace);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class match_char
{
public:
explicit match_char(char c) : c_(c) {}
template <typename Iterator>std::pair<Iterator, bool> operator()(
Iterator begin, Iterator end) const
{
Iterator i = begin;
while (i != end)
if (c_ == *i++)
return std::make_pair(i, true);
return std::make_pair(i, false);
}
private:
char c_;
};
namespace boost {
namespace asio {
template <> struct is_match_condition<match_char>: public boost::true_type {};
} } // namespace boost::asio
...
boost::asio::streambuf b;
boost::asio::read_until(s, b, match_char('a'));
自定义内存分配(Custom Memory Allocation)#
许多异步操作需要分配一个对象去存储操作的状态。 另外,程序中包含了易于识别的异步操作链。 半双工协议(如HTTP服务器)对每个客户端连接维护一个有单向操作链(先接收后发送)。 全双工协议有两条并行的链。程序应该能够根据这些特性,为链中的所有异步操作重用这些内存。
对于用户自定义的Handler对象的副本
h,若需要分配与该处理程序关联的内存,这将有一个使用get_associated_allocator的分配器(allocator)。例如
1
boost::asio::associated_allocator_t<Handler> a = boost::asio::get_associated_allocator(h);
1
2
3
4
5
6
7
8
9
10
11
12
13
14class my_handler
{
public:
// Custom implementation of Allocator type requirements.
typedef my_allocator allocator_type;
// Return a custom allocator implementation.
allocator_type get_allocator() const noexcept
{
return my_allocator();
}
void operator()() { ... }
};1
2
3
4
5
6
7
8
9
10
11
12namespace boost { namespace asio {
template <typename Allocator> struct associated_allocator<my_handler, Allocator>
{
// Custom implementation of Allocator type requirements.
typedef my_allocator type;
// Return a custom allocator implementation.
static type get(const my_handler&, const Allocator& a = Allocator()) noexcept
{
return my_allocator();
}
};
} } // namespace boost::asio
- 该实现保证,对于库中包含的异步操作,该实现不会对内存分配函数进行并发调用。如果需要从不同线程调用分配函数,将插入适当的内存屏障,以确保内存正确。