Boost.Asioで送受信/読み書き、とくに受信/読み込みを行う場合には、「バッファの消費」という考え方に気をつける必要があります。
以下のようなケースを考えてみます。
これは延々と受信を繰り返すだけの単純なプログラムです。
#include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/bind.hpp> namespace asio = boost::asio; using asio::ip::tcp; class Client { asio::io_service& io_service_; tcp::socket socket_; asio::streambuf receive_buffer_; public: Client(asio::io_service& io_service) : io_service_(io_service), socket_(io_service) {} void connect() { socket_.connect(tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 31400)); } void receive() { asio::async_read( socket_, receive_buffer_, asio::transfer_at_least(1), // 少なくても1バイト受信する boost::bind(&Client::on_receive, this, asio::placeholders::error)); } private: void on_receive(const boost::system::error_code& error) { // 受信したデータをstringに変換 const std::string data(asio::buffer_cast<const char*>(receive_buffer_.data()), receive_buffer_.size()); std::cout << data << std::endl; // 続けて受信する receive(); } }; int main() { asio::io_service io_service; Client client(io_service); client.connect(); client.receive(); io_service.run(); }
ここで、最初の受信で"abc"を受信した場合、on_receive()関数のdata変数には"abc"文字列が入ります。
on_receive()関数の中で続けて受信処理を行い、"def"が受信された場合、on_receive()関数のdata変数には"abcdef"文字列が入ります。
前回の受信データがそのまま残ってしまっています。
受信したバッファのうち、処理を行ったものは消費しなければなりません。
バッファを消費する方法は大きく2つあります:
- バッファをstd::istreamに結びつけ、ストリームで読み込み処理を行う。
読み込まれたバッファは消費されます。
void on_receive(const boost::system::error_code& error) { std::istream is(&receive_buffer_); std::string data; is >> data; // ストリームから読み込む:読み込んだ分が消費される receive(); }
- asio::streambuf::consume()関数を使用して先頭Nバイトを消費する
consume()は、指定したバイト数分のバッファを消費します。
これは主に、バッファの全てを一気に読み込み、バッファをクリアするために使用します。
void on_receive(const boost::system::error_code& error) { const std::string data(asio::buffer_cast<const char*>(receive_buffer_.data()), receive_buffer_.size()); receive_buffer_.consume(receive_buffer_.size()); // 受信バッファのサイズ分消費する receive(); }
バッファの消費という考え方は、受信関数の「少なくても」Nバイト、「少なくても」指定した文字まで受信するという仕様から、「まだ受信してない残りを受信する」というユーザーの設計を許可するためにあるのだと思いますが、この設計意図を把握してないとなかなか難しいかもしれません。
参照:
boost::asio::basic_streambuf::consume