2016-03-30 5 views
2

Asio:入力を受け取るために自動的にサイズ変更可能なバッファがありますか?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

どのような行でsegfaultsを知っていますか? – Tobias

+0

申し訳ありませんが、streambufと​​関係があります。 – Shuzheng

+0

まず、何桁のオクテット/バイトを記入するかを知っていれば、通常の固定サイズのバッファを使用していないのはなぜですか?第二に、私は 'boost :: asio :: async_read_until'や' boost :: asio :: async_read_some'を使うと、元の問題に関してより意味をなさないと思います。 – Tobias

答えて

4

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 
+0

優秀!それはstreambufを使用して行うことができますか? streambufの入力と出力のバッファの組み合わせによって混乱しています... atoi()を使用して解析ヘッダ関数を書き直すことはできますか?あなたはベクトルが配列として実装されていることを使用しています。私はvectorがbuffer()のように使用できることに気づいていませんでした。 – Shuzheng

+0

なぜio_serviceでリセットを呼びますか? – Shuzheng

+0

@Nicolasそれは確かにstreambufと​​atoi([demo](http://coliru.stacked-crooked.com/a/03f9f61943f979cd))で行うことができます。この[回答](http://stackoverflow.com/a/31992879/1053968)は 'streambuf'の理解に役立ちます。 –

関連する問題