3

I am trying to asynchronously send and receive custom data packets with boost and I have some questions, based on my current implementation:

tcpclient.cpp

#include "tcpclient.h"
#include <boost/chrono.hpp>
#include "../utils/logger.h"

tcpclient::tcpclient(std::string host, int port) :
        _endpoint(boost::asio::ip::address::from_string(host), port), _socket(_ioservice) {

    logger::log_info("Initiating client ...");
}

void tcpclient::start() {
    connect();
    _ioservice.run();
}

void tcpclient::connect() {
    _socket.async_connect(_endpoint, std::bind(&tcpclient::connect_handler, this, std::placeholders::_1));
}

void tcpclient::receive() {
    boost::asio::async_read(_socket, boost::asio::buffer(data, HEADER_LEN), std::bind(&tcpclient::read_handler, this, std::placeholders::_1));
}

void tcpclient::connect_handler(const boost::system::error_code &error) {
    if (!error) {
        _status = CONNECTED;
        logger::log_info("Connected to " + get_remote_address_string());
        receive();
    } else {
        _status = NOT_CONNECTED;
        _socket.close();
        _timer.expires_from_now(boost::asio::chrono::seconds{2});
        _timer.async_wait(std::bind(&tcpclient::reconnect_handler, this, std::placeholders::_1));
        logger::log_info("Failed to connect");
    }
}

void tcpclient::reconnect_handler(const boost::system::error_code &error) {
    connect();
}

void tcpclient::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
    logger::log_info("reading some data?");
}


std::string tcpclient::get_remote_address_string() {
    return _socket.remote_endpoint().address().to_string() + ":" + std::to_string(_socket.remote_endpoint().port());
}

tcpclient.h

#include <string>
#include <boost/asio.hpp>


enum ConnectionStatus{
    NOT_CONNECTED,
    CONNECTED
};


class tcpclient {

public:

    tcpclient(std::string host, int port);

    void start();

    void connect();

private:

    ConnectionStatus _status = NOT_CONNECTED;

    const int HEADER_LEN = 0x01;

    void receive();

    void connect_handler(const boost::system::error_code& error);

    void reconnect_handler(const boost::system::error_code &error);

    void read_handler(const boost::system::error_code& error);

    std::string get_remote_address_string();


    boost::asio::io_service _ioservice;
    boost::asio::ip::tcp::endpoint _endpoint;
    boost::asio::ip::tcp::socket _socket;
    boost::asio::steady_timer _timer{_ioservice, boost::asio::chrono::seconds{2}};

};

1) Would it make sense to add two seperate handlers for reading and writing, one for the header one for the body?

2) How can I ensure that I can send and receive data at the same time without running into any issues? Does this require multiple sockets? How is such functionality usually implemented?

3) How would I go sending custom data structures? I want to implement a custom packet structure. How would I sent such objects, does boost have an inbuilt serializer? How or custom data structures sent and received?

4) What happens if the connection drops during sending or receiving data? Are such exceptions handled directly in the receive/send handlers by checking the errorcode object for eof?

A basic example for that would help a lot!

Kyu96
  • 1,159
  • 2
  • 17
  • 35
  • 1
    Suggest you join the cpplang slack group for this kind of question: https://cpplang.slack.com/messages/C27KZLB0X – Richard Hodges Apr 14 '19 at 14:52
  • @RichardHodges Thanks for the reply but I don't really use slack, but I'll have a look. I was hoping for someone experienced with boost to answer directly on SOF. – Kyu96 Apr 16 '19 at 11:32

1 Answers1

3

1) Would it make sense to add two seperate handlers for reading and writing, one for the header one for the body?

Yes. Code is different, different handlers is best practice.

2) How can I ensure that I can send and receive data at the same time without running into any issues?

Async operations. The functions return immediately, so the only delay is the network speed.

3) How would I go sending custom data structures?

boost::asio::buffer supports sending/receiving raw memory bytes. When this isn't enough, asio has serialization:

4) What happens if the connection drops during sending or receiving data?

All read and write operations return a boost::system::error_code. If an error occurs, the socket must be closed (and connected again if possible). Some basic code:

void AfterReadingHeader(const boost::system::error_code & aError)
{
  if (aError)
  {
    // something went wrong 
    boost::system::error_code error;
    socket.shutdown(tcp::socket::shutdown_receive, error);
    socket.close(error);
  }

  ...
}
Cosmin
  • 21,216
  • 5
  • 45
  • 60
  • Regarding 2) Does this not require two separate sockets? So can one socket send and receive data at the same time? Also can you maybe provide a basic example of how to write and read the n-byte header and then the body afterwards? I am unsure how to chain both operations properly so that first the header is processed and then body (only if the header was ok). – Kyu96 Apr 17 '19 at 12:44
  • A socket supports both send and receive, no need to have two. The chat server/client is a good example for header/body messages: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/examples/cpp11_examples.html – Cosmin Apr 17 '19 at 15:16