基于行的操作
许多广泛应用的网络协议是基于行的,这意味着许多协议元素通过"\r\n"来分割,比如HTTP、SMPT、FTP,为了更方便的实现基于行的协议,和其他基于分隔符的协议一样,Boost::Asio包括了read_until() 和async_read_until()。
下面的例子阐述了async_read_until() 在HTTP server的应用,为了从HTTP client接收第一行
class 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_;
};
streambuf的数据成员保存了从socket接收到的没有被分割的数据,在分隔符后面还可能有很多数据,多余的数据应当留在streambuf 中,并使用read_until() 或者 async_read_until()来检查子序列
分隔符可能是一个单独的char,一个std::string或者boost::regex,read_until() 和 async_read_until() 也包含了用户自定义函数对象作为匹配条件的重载,比如,一直读数据直到遇到一个空白字符
typedef 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);
从流缓存中读数据,知道遇到一个匹配的字符
class 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'));
is_match_condition<> 自动评估函数的结果是true还是false,以及拥有前台的result_type 定义的函数对象。其他类型必须显式指定,就像上面写的那样