4

私は既存の例を研究しました:

  1. boost::asio を使用した Protobuf メッセージの送信
  2. boost::asio::read_async を使用して Protobuf オブジェクトを読み取る
  3. Google プロトコル バッファ: C++ の parseDelimitedFrom と writeDelimitedTo
  4. Java の Protocol Buffers で区切られた I/O 関数に相当する C++ はありますか?
  5. boost::asio を使用した Protobuf メッセージの送信

しかし、Boost::asio API を使用して Google Protobuf メッセージを渡す方法がまだわかりません。特に、次の問題について明確な理解がありません。

  1. boost::asio::streambuf と google::protobuf::io オブジェクト間の相互作用 (および最後のものを適用する必要性)
  2. メッセージ ストリーミングの正しい実装 (C++ API に writeDelimitedTo および parseDelimitedFrom メソッドがないため)

これは、サンプルのboost::asio v. 1.39 ssl_clientに基づく私の実装です。

    class client
{
public:
  client(boost::asio::io_service& io_service, boost::asio::ssl::context& context,
      boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
    : socket_(io_service, context),
        request_stream(&b),
        raw_output(&request_stream),
        coded_output(&raw_output)
  {
    ... 
  }

  void handle_connect(const boost::system::error_code& error,
      boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
  {
    ...
  }

  //Debugging function
  void print_buffers_condition(const char *step)
  {
      std::cout << "\nBuffer conditions after " << step << std::endl;
      std::cout << "boost::asio::streambuf\t\tb: " << b.size() << std::endl;
      std::cout << "google::protobuf::io::OstreamOutputStream raw_output: " << raw_output.ByteCount() << std::endl;
      std::cout << "google::protobuf::io::CodedOutputStream coded_output: " << coded_output.ByteCount() << std::endl;
      std::cout << std::endl;
  }

  //Sending test message after SSL Handshake
  void handle_handshake(const boost::system::error_code& error)
  {
      std::cout << "-----------------------------SENDING-----------------------------" << std::endl;
    print_buffers_condition("handle handshake");
    if (!error)
    {
        SearchRequest msg;
        msg.set_query("qwerty");
        msg.set_code(12345);

        std::cout << "Debugged" << std::endl;
        msg.PrintDebugString();


        //Writing the length of the message before and serializing                 
                    print_buffers_condition("before serialising");
        coded_output.WriteVarint32(msg.ByteSize());
        if (!msg.SerializeToCodedStream(&coded_output))
        {
            std::cout << "serailizing error" << std::endl;
        }
        else
        {
            std::cout << "serializing success" << std::endl;
        }

        //Sending
        buffers_condition("before async write");
        boost::asio::async_write(socket_,
                                 b,
                                 boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        buffers_condition("after async write");
    }
    else
    {
      std::cout << "Handshake failed: " << error << "\n";
    }
  }

  void handle_write(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    std::cout << " bytes_trransferred: " << bytes_transferred << std::endl;
    if (!error)
    {
        std::cout << "No error" << std::endl;
        ...
    }
    else
    {
      std::cout << "Write failed: " << error << "\n";
    }
  }

  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    ...
  }

private:
  boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
  boost::asio::streambuf b;
  std::ostream request_stream;
  google::protobuf::io::OstreamOutputStream raw_output;
  google::protobuf::io::CodedOutputStream coded_output;
};

このコードは機能するので、メッセージを作成した後、void handle_write(const boost::system::error_code& error, size_t bytes_transferred)関数に入ります。値を出力bytes_transferred_すると 0 が返されます: サーバー (に基づいて実装されています) は何も受信しません。

デバッグ関数の使用は、void print_buffers_condition(const char *step)さまざまなバッファリング オブジェクトのスタックを介した送信中にメッセージが失われることを示唆しています。

    $ ./client 127.0.0.1 5000
-----------------------------SENDING-----------------------------

Buffer conditions after handle handshake
boost::asio::streambuf      b: 0
google::protobuf::io::OstreamOutputStream raw_output: 8192
google::protobuf::io::CodedOutputStream coded_output: 0

Debugged: 
query: "qwerty"
code: 12345

Buffer conditions after before serialization
boost::asio::streambuf      b: 0
google::protobuf::io::OstreamOutputStream raw_output: 8192
google::protobuf::io::CodedOutputStream coded_output: 0

serializing success

Buffer conditions after before async write
boost::asio::streambuf      b: 0
google::protobuf::io::OstreamOutputStream raw_output: 8192
google::protobuf::io::CodedOutputStream coded_output: 13


Buffer conditions after after async write
boost::asio::streambuf      b: 0
google::protobuf::io::OstreamOutputStream raw_output: 8192
google::protobuf::io::CodedOutputStream coded_output: 13

 bytes_trransferred: 0

適切な方法でそれを行う方法がわかりません。OSはRHEL6.4です。ありがとうございました。

4

1 に答える 1

4

私はasioに精通していませんが、バッファをフラッシュしていないことが問題のようです。データは立ち往生してCodedOutputStreamおり、asio に到達することはありません。

CodedOutputStreamメッセージの書き込みが完了するとすぐに破棄されるように、スタックに割り当てる必要があります。デストラクタはバッファをフラッシュします。CodedOutputStream割り当てが安価であるため、スタックに配置してもパフォーマンスの問題はありません (実際、その方がおそらく優れています) 。

OstreamOutputStream同様にスタックに割り当てることができますが、再利用したいバッファをヒープに割り当てます。同じオブジェクトを再利用することを選択した場合は、が破棄されたFlush()後にバッファーをフラッシュするために呼び出してください。CodedOutputStream

ちなみに、既に行っているOstreamOutputStreamことの上に独自のバッファリングレイヤーを実行する必要があるため、特に効率的ではありません。文字列 (または)ostreamにシリアライズしてから、それをソケットに直接書き込むことができます (asio がこれを許可している場合)。これにより、冗長なコピーが回避される可能性があります。str = message.SerializeAsString()message.SerializeToString(&str)

于 2013-11-07T22:16:07.810 に答える