2011-01-26 21 views
9

Estoy tratando de hackear un cliente en C++ usando los Buffers de Protocolo de Google y boost :: asio.Enviando mensajes de Protobuf con boost :: asio

Mi problema es que no sé cómo puedo enviar el mensaje de protobuf a asio. Lo que tengo es la siguiente:

// set up *sock - works 
PlayerInfo info; 
info.set_name(name); 
// other stuff 

Ahora sé que lo siguiente es incorrecto, pero lo voy a publicar de todos modos:

size_t request_length = info.ByteSize(); 
boost::asio::write(*sock, boost::asio::buffer(info, request_length)); 

que tiene la medida de lo que sé que tengo que empacar mi mensaje de manera diferente en el búfer, pero ¿cómo?

En general, me está resultando difícil descifrar cómo funciona boost :: asio. Hay algunos tutoriales, pero normalmente solo cubren el envío de formatos de datos estándar, como ints, que funciona de fábrica. Pensé que mi problema es serialización, pero por otro lado aprendí que protobuf debería hacer esto por mí ... y ahora estoy confundido;)

¡Gracias por su ayuda!

-> Daniel Gehriger brindó la solución, ¡muchas gracias!

Respuesta

8

No sé mucho acerca de búfer de protocolo de Google, pero trata de lo siguiente:

PlayerInfo info; 
info.set_name(name); 
// ... 

boost::asio::streambuf b; 
std::ostream os(&b); 
info.SerializeToOstream(&os); 

boost::asio::write(*sock, b); 
+5

Necesitará agregar algo más de información a la transmisión para que pueda leer de manera confiable el objeto PlayerInfo en el otro extremo. Protobuf no incrusta directamente el tipo de objeto en la secuencia. No creo que contenga la longitud tampoco, pero tendría que verificar la documentación. Por cierto, ASIO no sabe acerca de los objetos; solo trata con bytes. Tenga esto en cuenta cuando lea los ejemplos y la documentación. – Dan

+0

Daniel Gehriger: ¡Muchas gracias! ¡Eso funciono! – adi64

+0

Dan: Sí, pensé que tenía que agregar el tamaño de antemano para que el servidor pueda leerlo ... ¡Gracias! – adi64

2

que acaba de comenzar usando Google Protocol Buffers (protobuf) y también tenía problemas para enviar (y recibir) mensajes a través de una red informática.

En contraste con la API de Java, la API de C++ no tiene un método writeDelimitedTo para enviar un mensaje de protobuf con un delimitador. Sin un delimitador también tenemos que enviar el tamaño del mensaje, para poder deserializarlo en el punto final de recepción.

La API C++ ofrece la clase ::google::protobuf::io::CodedOutputStream, definida en el archivo de encabezado google/protobuf/io/coded_stream.h.

El siguiente código fuente muestra cómo enviar un mensaje de protobuf delimitado a través del Boost.Asio. El ejemplo usa UDP. Como no he encontrado un ejemplo de trabajo en la WWW, lo comparto aquí.

#include "boost/asio.hpp" 
#include "google/protobuf/io/coded_stream.h" 
#include "google/protobuf/io/zero_copy_stream_impl.h" 

using ::boost::asio::ip::udp; 

int main() { 
    PlayerInfo message; 
    message.set_name("Player 1"); 
    // ... 

    const boost::asio::ip::address_v4 kIpAddress = boost::asio::ip::address_v4::loopback(); 
    const unsigned short kPortNumber = 65535; 

    try { 
    boost::asio::io_service io_service; 
    udp::socket socket(io_service, boost::asio::ip::udp::v4()); 

    udp::endpoint endpoint(kIpAddress, kPortNumber); 
    boost::system::error_code error; 

    boost::asio::streambuf stream_buffer; 
    std::ostream output_stream(&stream_buffer); 

    { 
     ::google::protobuf::io::OstreamOutputStream raw_output_stream(&output_stream); 
     ::google::protobuf::io::CodedOutputStream coded_output_stream(&raw_output_stream); 
     coded_output_stream.WriteVarint32(message.ByteSize()); 

     message.SerializeToCodedStream(&coded_output_stream); 
     // IMPORTANT: In order to flush a CodedOutputStream it has to be deleted, 
     // otherwise a 0 bytes package is send over the wire. 
    } 
    } 

    size_t len = socket.send_to(stream_buffer.data(), endpoint, 0, error); 

    if (error && error != boost::asio::error::message_size) { 
    throw boost::system::system_error(error); 
    } 

    std::cout << "Sent " << len << " bytes data to " << kIpAddress.to_string() << "." << std::endl; 
} catch (const std::exception& ex) { 
    std::cerr << ex.what() << std::endl; 
} 

Mientras escribía este artículo, también he descubierto las siguientes dos preguntas:

ambos están relacionados con esta cuestión y también contienen (parcial) respuestas. Espero que mi respuesta sea útil de todos modos.

Cuestiones relacionadas