1

i have an understanding problem how boost asio handles this:

enter image description here

When I watch my request response on client side, I can use following boost example Example

But I don't understand what happens if the server send every X ms some status information to the client. Have I open a serperate socket for this or can my client difference which is the request, response and the cycleMessage ?

Can it happen, that the client send a Request and read is as cycleMessage? Because he is also waiting for async_read because of this Message?


class TcpConnectionServer : public boost::enable_shared_from_this<TcpConnectionServer>
{
public:
    typedef  boost::shared_ptr<TcpConnectionServer> pointer;
    static pointer create(boost::asio::io_service& io_service)
    {
        return pointer(new TcpConnectionServer(io_service));
    }
    boost::asio::ip::tcp::socket& socket()
    {
        return m_socket;
    }
    void Start()
    {
        SendCycleMessage();
        boost::asio::async_read(
                m_socket, boost::asio::buffer(m_data, m_dataSize),
                boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
    }

private:
    TcpConnectionServer(boost::asio::io_service& io_service)
        : m_socket(io_service),m_cycleUpdateRate(io_service,boost::posix_time::seconds(1))
      {

      }
    void handle_read_data(const boost::system::error_code& error_code)
    {
        if (!error_code)
        {
        std::string answer=doSomeThingWithData(m_data);
        writeImpl(answer);

        boost::asio::async_read(
                m_socket, boost::asio::buffer(m_data, m_dataSize),
                boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
        }
        else
        {

            std::cout << error_code.message() << "ERROR DELETE READ \n";
            // delete this;
        }
    }


    void SendCycleMessage()
    {
        std::string data = "some usefull data";
        writeImpl(data);
        m_cycleUpdateRate.expires_from_now(boost::posix_time::seconds(1));
        m_cycleUpdateRate.async_wait(boost::bind(&TcpConnectionServer::SendTracedParameter,this));
    }

    void writeImpl(const std::string& message)
    {
        m_messageOutputQueue.push_back(message);
        if (m_messageOutputQueue.size() > 1)
        {
            // outstanding async_write
            return;
        }

        this->write();
    }

    void write()
    {
        m_message = m_messageOutputQueue[0];
        boost::asio::async_write(
                m_socket,
                boost::asio::buffer(m_message),
                boost::bind(&TcpConnectionServer::writeHandler, this, boost::asio::placeholders::error,
                            boost::asio::placeholders::bytes_transferred));
    }

    void writeHandler(const boost::system::error_code& error, const size_t bytesTransferred)
    {
        m_messageOutputQueue.pop_front();
        if (error)
        {
            std::cerr << "could not write: " << boost::system::system_error(error).what() << std::endl;
            return;
        }

        if (!m_messageOutputQueue.empty())
        {
            // more messages to send
            this->write();
        }
    }

    boost::asio::ip::tcp::socket m_socket;
    boost::asio::deadline_timer m_cycleUpdateRate;
    std::string m_message;

    const size_t m_sizeOfHeader = 5;
    boost::array<char, 5> m_headerData;
    std::vector<char> m_bodyData;

    std::deque<std::string> m_messageOutputQueue;
};

With this implementation I will not need boost::asio::strand or? Because I will not modify the m_messageOutputQueue from an other thread.

But when I have on my client side an m_messageOutputQueue which i can access from an other thread on this point I will need strand? Because then i need the synchronization? Did I understand something wrong?

Hunk
  • 479
  • 11
  • 33

1 Answers1

2

The differentiation of the message is part of your application protocol.

ASIO merely provides transport.

Now, indeed if you want to have a "keepalive" message you will have to design your protocol in such away that the client can distinguish the messages.

The trick is to think of it at a higher level. Don't deal with async_read on the client directly. Instead, make async_read put messages on a queue (or several queues; the status messages could not even go in a queue but supersede a previous non-handled status update, e.g.).

Then code your client against those queues.

A simple thing that is typically done is to introduce message framing and a message type id:

FRAME offset 0: message length(N)
FRAME offset 4: message data
FRAME offset 4+N: message checksum
FRAME offset 4+N+sizeof checksum: sentinel (e.g. 0x00, or a larger unique signature)

The structure there makes the protocol more extensible. It's easy to add encryption/compression without touch all other code. There's built-in error detection etc.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank your for your answer. One Question which is not clear to me: is it not possible that my client do async_write as request and the client async_read gets it? Is it always save that just the server read and write of the client? What happens if my client and my server write on the same time? – Hunk Jun 30 '15 at 09:28
  • No that won't happen. Stream sockets are a full duplex channel. What you write on one end comes out (only) on the other end – sehe Jun 30 '15 at 09:36
  • The client and server have different sockets anyway (assuming you're not actually writing both in the same process...). Inside the same process you need to synchronize operations on a single socket. – sehe Jun 30 '15 at 09:36
  • I assume that inside one instance of io_service.run() i don't have to synchronize or? I tried on my client side an async_read which read all the time my socket and i have a sendMessage method which write data to the socket when i get a user input. I thought that boost asio makes the synchronization? – Hunk Jul 02 '15 at 10:10
  • What do you mean "inside run()"? That's library internal. If you mean, "only one thread running `io_service::run()` then, yes, you have what is known as [an **implicit strand**](http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/overview/core/strands.html). See also http://stackoverflow.com/questions/12794107/why-do-i-need-strand-per-connection-when-using-boostasio/12801042#12801042 – sehe Jul 02 '15 at 10:14
  • My Problem was that I misunderstand, I edited my post above to make it clear. But i Think i understand it now thank you – Hunk Jul 02 '15 at 15:37