3
votes

I am an AspNet programmer with 57 years of age. Because I was the only one who worked a little, back in the beginning, with C ++, my bosses asked me to serve a customer who needs a communication agent with very specific characteristics. It can run as a daemon on multiple platforms and be both client and server at times. I do not know enough but I have to solve the problem and found a chance in the Boost / Asio library.

I am new to Boost-Asio and reading the documentation I created a server and a TCP socket client that exchanges messages perfectly and two-way, full duplex.

I read several posts where they asked for the same things I want, but all the answers suggested full duplex as if that meant having a client and a server in the same program. And it's not. The definition of full duplex refers to the ability to write and read from the same connection and every TCP connection is full duplex by default.

I need to make two programs can accept connections initiated by the other. There will be no permanent connection between the two programs. Sometimes one of them will ask for a connection and at other times the other will make this request and both need to be listening, accepting the connection, exchanging some messages and terminating the connection until new request is made.

The server I did seems to get stuck in the process of listening to the port to see if a connection is coming in and I can not continue with the process to be able to create a socket and request a connection with the other program. I need threads but I do not know enough about them.

It'is possible?

As I said I'm new to Boost / Asio and I tried to follow some documents of threads and Coroutines. Then I put the client codes in one method and the server in another.:

int main(int argc, char* argv[])
{
    try
    {
        boost::thread t1(&server_agent);
        boost::thread t2(&client_agent);

        // wait
        t1.join();
        t2.join();   
        return 0;       
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }    
    return 0;
}

and two Coroutines:

void client_agent() {
    parameters param;
    param.load();

    boost::asio::io_service io_service1;
    tcp::resolver resolver(io_service1);
    char port[5];
    _itoa(param.getNrPortaServComunic(), port, 10);
    auto endpoint_iterator = resolver.resolve({ param.getIPServComunicPrincipal(), port });
    std::list<client> clients;
    client c(io_service1, endpoint_iterator, param);

    while (true)
    {
        BOOL enviada = FALSE;
        while (true) {
            if (!enviada) {
                std::cout << "sending a message\n";
                int nr = 110;
                message msg(nr, param);
                c.write(msg);
                enviada = TRUE;
            }
        }
    }

    c.close();
}

void server_agent() {

    parameters param;
    param.load();

    boost::asio::io_service io_service1;
    std::list<server> servers;
    tcp::endpoint endpoint(tcp::v4(), param.getNrPortaAgenteServ());
    servers.emplace_back(io_service1, endpoint);
    io_service1.run();
}

I used one port to client endpoint and other port to server endpoint. Is it correct? Required?

It starts looking like it's going to work. Each of the methods runs concurrently but then I get a thread allocation error at the io_service1.run (last line of the server_agent method):

boost::exception_detail::clone_impl > at memory location 0x0118C61C.

Any suggestion?

1
As for the title, I'm pretty sure you can do a full duplex server/client client/server implementation with boost asio. Just enable a boost::acceptor for both processes and a client conection. Just handle everything asynchronously.πάντα ῥεῖ
Thank you. I'll try this approach. It seems to be the only way to not get stuck in a loop.Neumann

1 Answers

1
votes

You are describing a UDP client/server application. But your implementation is bound to fail. Think of an asio server or client as always running in a single thread.

The following code is just so you get an idea. I haven't tried to compile it. Client is very similar, but may need a transmit buffer, depends on the app, obviously.

This is a shortened version, so you get the idea. In a final application you way want to add receive timeouts and the likes. The same principles hold for TCP servers, with the added async_listen call. Connected sockets can be stored in shared_ptr, and captured by the lambdas, will destroy almost magically.

Server is basically the same, except there is no constant reading going on. If running both server and client in the same process, you can rely on run() to be looping because of the server, but if not, you'd have to call run() for each connection. run() would exit at the end of the exchange.

using namespace boost::asio;  // Or whichever way you like to shorten names

class Server
{
  public:
    Server(io_service& ios) : ios_(ios) {}

    void Start()
    {
      //  create socket
      //  Start listening
      Read();
    }

    void Read()
    {
        rxBuffer.resize(1024)
        s_.async_receive_from(
            buffer(rxBuffer),
            remoteEndpoint_,
            [this](error_code ec, size_t n)
        {
            OnReceive(ec, n);  // could be virtual, if done this way
        });
    }

    void OnReceive(error_code ec, size_t n)
    {
        rxBuffer_.resize(n);
        if (ec)
        {
          // error ... stops listen loop
          return;
        }

        // grab data, put in txBuffer_
        Read();
        s_.async_send_to(
            buffer(txBuffer_),
            remoteEndpoint_,
            [this, msg](error_code ec, size_t n)
        {
            OnTransmitDone(ec, n);
        });
    }

  void OnTransmitDone(error_code ec, size_t n)
  {
     // check for error?
     txBuffer_.clear();
  }

  protected:
    io_service& ios_;
    ip::udp::socket s_;   
    ip::udp::endpoint remoteEndpoint_;  // the other's address/port
    std::vector<char> rxBuffer_;        // could be any data type you like
    std::vector<char> txBuffer_;        // idem  All access is in one thread, so only 
                                        // one needed for simple ask/respond ops.
};

int main()
{
  io_service ios;
  Server server(ios);    // could have both server and client run on same thread
                         // on same io service this way.

  Server.Start();

  ios_run();
  // or std::thread ioThread([&](){ ios_.run(); });
  return 0;
}