0
votes

I don't really know how to put this question, but here we go.

So let's assume I'm using ASIO from Boost libs to connect to a server using a TCP socket and write a certain message to it.

The message contains some user data and looks like this: 1 byte (packet ID), 4 bytes (integer), user null-terminated string and 6 zero bytes (reserved by the server but unused).

What would be the most convenient way to assemble such a message for use with ASIO's boost::asio::buffer function?

I'm really confused at this point. Help greatly appreciated.

3
Your message is a string or structure? - ForEveR
That's what I actually can't decide with. I have tried using a string, but I'm not sure what would be the most appropriate way to fill it with necessary byte data, and especially if it's at all the best option to go with, assuming ASIO reference states that it may work just as well with std::vector or std::string. I thought of using a struct for this, but I'm not totally sure about how to do it properly. - Samuel Moriarty

3 Answers

2
votes

Instead of making a single buffer using asio::buffer() function, you can adapt your structure to be a buffer sequence - asio functions accept such a concept. It would be convenient to send this way a pattern like fixed--null-terminated--null-terminated-fixed-fixed-etc...

1
votes

A. Define packet structures that can be serialized.

class ISerializable
{
public:
    virtual ~ISerializable(){}

    virtual void serialize(std::ostream& stream) = 0;
};

class LoginPacket : public ISerializable
{
public:
    // Constructor and access functions

    virtual void serialize(std::ostream& stream)
    {
        stream.write((const char*)&packetId, 1);
        stream.write((const char*)&accountId, 4);
        stream.write(username.c_str(), username.size() + 1);
        // Write the unused 6-bytes of zero
        int zero(0);
        stream.write((const char*)&zero, 4);
        stream.write((const char*)&zero, 2);
    }

private:
    unsigned char packetId;
    unsigned int32_t accountId;
    std::string username;
};

B. Now, to use this packet class do something like:

LoginPacket packet;
// Fill details for the packet
std::stringstream data;
packet.serialize(data);
// Send the data to the network
yourSocket.send(data.str().c_str(), data.str().size());
1
votes

the boost::asio::buffer() functions adapt or convert other kind of buffers to the one, used by asio.

You should use some kind of application buffer, fill that buffer and then pass the buffer to asio for writing onto the wire. For example:

std::vector< char > data;

data.push_back( id );
data.push_back( i & 0xff );
data.push_back( ( i >> 8 ) & 0xff );
data.push_back( ( i >> 16 ) & 0xff );
data.push_back( ( i >> 24 ) & 0xff );

const char* const str = s.c_str();
data.insert( data.end(), str, str + std::strlen( str ) );

for ( int pad = 0; pad != 4; ++pad )
    data.push_back( 0 );

boost::asio::write( socket, boost::asio::buffer( data ) );