Home > database >  Boost Sockets message exchange c
Boost Sockets message exchange c

Time:10-21

I am currently trying to create a server to client connection to send XML documents. It appears that it is possible to send these documents after serializing them. My plan is to establish a connection, send one message from client to the server (a list of filters) and then send N messages from the server side when a message meets the filters criterias.

However, before getting to that, I wanted to try a simple implementation of this using only strings to see how it works and to get a better understanding of the boost asio library.

I then implemented my server part:

#include <iostream>
#include <thread>
#include <queue>
#include <chrono>
#include <array>
#include <boost/asio.hpp>

int main()
{
    const int BACKLOG_SIZE = 30;
    unsigned short PORT = 3333;

    // Endpoint and io_service creation
    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(),PORT);
    boost::asio::io_service ios;

    boost::asio::ip::tcp::acceptor acceptor(ios, ep.protocol());

    // Binding to client
    acceptor.bind(ep);

    acceptor.listen(BACKLOG_SIZE);

    boost::asio::ip::tcp::socket socket(ios);
    // accepting connection (blocking process) 
    acceptor.accept(socket);

    std::cout << "Binded" << std::endl;

    // Receiving data from client

    boost::asio::streambuf sb;
    boost::system::error_code ec;
    
    while (boost::asio::read(socket, sb, ec))
    {
        std::cout << "received: " << &sb << "\n";
        if (ec)
        {
            std::cout << "status: " << ec.message() << "\n";
            break;
        }
    }

    // Sending data to client

    socket.send("Filters received!");

}

and my client part:

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

int main()
{
    std::string raw_ip_address = "127.0.0.1";
    unsigned short port_num = 3333;
    std::string FILTER = "ATL";

    try {
        // Endpoint creation
        boost::asio::ip::tcp::endpoint
            ep(boost::asio::ip::address::from_string(raw_ip_address),
                port_num);

        boost::asio::io_service ios;

        // Creating and opening a socket.
        boost::asio::ip::tcp::socket sock(ios, ep.protocol());

        // Connecting a socket.
        sock.connect(ep);

        // Sending data to server

        std::cout << "Connected to " << raw_ip_address << " Port: " << port_num << std::endl;

        sock.send(boost::asio::buffer(FILTER));

        boost::asio::streambuf sb;
        boost::system::error_code ec;

        // Receiving data from server

        while (boost::asio::read(socket, sb, ec))
        {
            std::cout << "received: " << &sb << "\n";
            if (ec)
            {
                std::cout << "status: " << ec.message() << "\n";
                break;
            }
        }
    }
    // Overloads of asio::ip::address::from_string() and 
    // asio::ip::tcp::socket::connect() used here throw
    // exceptions in case of error condition.
    catch (boost::system::system_error& e) {
        std::cout << "Error occured! Error code = " << e.code()
            << ". Message: " << e.what();

        return e.code().value();
    }

    return 0;
}

I first started with only sending the filters from the client to the server and it all worked well. However when I tried adding the response from the server to the client, it caused an error which I don't really understand: error

enter image description here

As anyone faced this issue and know how to solve it? Is it even possible to establish a connection like that where both side of the socket can send messages as they want (as I said the idea will be to have the server sending N messages coming from another application, after receiving the filters list).

Thanks for your help.

CodePudding user response:

First problem: string literals are not a buffer (or buffer sequence).

You need to describe the buffer, e.g. like so:

std::string response("Filters received!");
socket.send(boost::asio::buffer(response));

There are many ways. E.g. not using a temporary, you could use a string view literal:

socket.send(boost::asio::buffer("Filters received!"sv));

Second problem:

    while (boost::asio::read(socket, sb, ec)) {

This cannot work, because you meant sock, not socket (which exists, but is ::socket, type int(&)(int,int,int).

That said, it's not very useful to read-to-EOF in a while loop, since the loop will always break with EOF (or another error).

Finally, because currently, client will never close the socket until a response is received, there will be an indefinite waiting of both programs. Consider a partial shutdown:

    sock.send(asio::buffer(FILTER));
    sock.shutdown(tcp::socket::shutdown_send);

Here are client and server combined

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
using namespace std::string_view_literals;

static const std::string raw_ip_address = "127.0.0.1";
static const std::string FILTER         = "ATL";
static const int         BACKLOG_SIZE   = 30;
static const uint16_t    PORT           = 3333;

int main(int argc, char**) {
    asio::io_service ios;
    asio::streambuf  sb;
    error_code       ec;

    try {
        if (argc > 1) { // client
            tcp::endpoint ep({}, PORT);

            tcp::socket sock(ios, ep.protocol());
            sock.connect(ep);

            // Sending data to server
            std::cout << "Connected to " << sock.remote_endpoint() << std::endl;

            sock.send(asio::buffer(FILTER));
            sock.shutdown(tcp::socket::shutdown_send);

            read(sock, sb, ec);
            std::cout << "received: " << &sb << "\n"
                      << "status: " << ec.message() << "\n";
        } else // server
        {
            tcp::acceptor acceptor(ios, tcp::v4());
            acceptor.bind({{}, PORT});
            acceptor.listen(BACKLOG_SIZE);

            tcp::socket sock(ios);
            acceptor.accept(sock);

            std::cout << "Accepted " << sock.remote_endpoint() << std::endl;

            read(sock, sb, ec);
            std::cout << "received: " << &sb << "\n"
                      << "status: " << ec.message() << "\n";

            sock.send(asio::buffer("Filters received!"sv));
        }
    } catch (boost::system::system_error& e) {
        std::cout << "Error occured! Error code = " << e.code()
                  << ". Message: " << e.what() << "\n";

        return 1; // e.code().value() not very useful, as the category is lost
    }
}

Run and build with e.g.

g   -std=c  20 -O2 -Wall -pedantic -pthread main.cpp -isystem /usr/local/include/ 
./a.out&
sleep 1; ./a.out client

Prints e.g.

Connected to 127.0.0.1:3333
Accepted 127.0.0.1:50816
received: ATL
status: End of file
received: Filters received!
status: End of file
  • Related