Where to implement the protocol using boost :: asio?

I am trying to implement a simple serial port protocol. This happens as follows:

  • discard all data until 0xff is received
  • read the header (node ​​address and data length, 4 bytes)
  • reading data (maximum 64 bytes)
  • read crc
  • process received packet
  • send response
  • when 0xff displayed, even if it is not expected, as in the middle of the data, it means that a new packet has been received.

I can implement this using boost::asio::serial_port with boost::asio::read() , reading one byte and processing that byte when it is received. Although this works, I was wondering if there is a way to “zoom in” to do this?

I looked at boost::asio::read_until() to read to 0xff , but then I do not know how to discard the data. Storing data in a buffer and then not using a buffer seems a bit wasteful.

I can use boost::asio::read_until() to read to the end of the package, but then MatchCondition must have access to the (buffer) in the buffer. MatchCondition seems to only get the iterator of the first and last bytes received recently.

In addition, the data received with boost::asio::read() ends with stream_buf , and I have to parse the received data into a Packet object. I can do this parsing inside a Packet in a separate ParsePacket object ParsePacket or somehow integrate it with boost::asio (something like boost::asio::read(serial, myPacket); where myPacket is a Packet object)

When 0xff displayed anywhere in the received data, it means that a new package is starting. Therefore, when 0xff received, it must forget any previous data received and start receiving a new packet.

I plan to use asynchronous operations and add timeouts.

So my question is: where to implement such a protocol? Or, more generally, where to implement the protocol using boost::asio . I'm not looking for working code, but something like a recommendation on where to implement the protocol and what functions of boost::asio use.

update:

In this case, no flow control (hardware or software) is used.

+8
c ++ protocols boost-asio
source share
2 answers

First of all, since @Autopulated points to comments, I want to warn you about using delimiters (your 0xFF) in binary protocols. This is a dangerous technique (brings a lot of ambiguity) and requires complex implementations. Even if you can guarantee that your data does not contain 0xFF bytes, you cannot do this in the CRC field.

I would recommend not to worry about any delimiters and focus on a simple and predictable binary protocol: [packet][packet]... where [packet] = [node address:X][data length:4][data:data length][CRC:1]

Sending such packages may look like this:

 size_t const data_length_bytes = 4; std::vector<char> data = ...; size_t data_length = data.size(); Node_address node_address = ...; std::vector<boost::asio::const_buffer> bufs; bufs.push_back(boost::asio::buffer(&node_address, sizeof(node_address))); bufs.push_back(boost::asio::buffer(&data_length, data_length_bytes)); bufs.push_back(boost::asio::buffer(data)); boost::system::error_code error; boost::asio::write(socket, boost::asio::buffer(bufs), error); if (error) throw boost::system::system_error(error); 

Reception:

 size_t data_length; std::vector<char> data; Node_address node_address; char crc; std::vector<boost::asio::mutable_buffer> bufs; boost::system::error_code error; bufs.push_back(boost::asio::buffer(&node_address, sizeof(node_address))); bufs.push_back(boost::asio::buffer(&data_length, data_length_bytes)); boost::asio::read(serial_port, bufs, error); if (error) throw boost::system::system_error(error); data.resize(data_length); bufs.clear(); bufs.push_back(boost::asio::buffer(&data.front(), data_length)); bufs.push_back(boost::asio::buffer(&crc, sizeof(crc)); boost::asio::read(serial_port, bufs, error); if (error) throw boost::system::system_error(error); // check CRC // send response 

Note that this example assumes that both peers have the same endianess.

I wrote this code here, so don't expect it to be correct, use it as an idea.

+2
source share

When implementing the protocols, I previously discovered that the state of the machine is very useful.

Until I used asio , this method may be applicable here.

They can be implemented in C ++ as enum , loop, and switch as follows:

 enum States { StateInitial, StateHeader, StateData ... }; States state = StateInitial; while (1) { char ch = get_byte_function(); switch (state) { case StateInitial: if (ch == '\xFF') state = StateHeader; break; case StateHeader: ... } } 

You will need to add a few more flags to track your progress in the protocol.

You can also see boost::statechart for the implementation of the state machine.

0
source share

All Articles