1
votes

Hello I ve been trying to implement a simple server/client app to communicate through UDP socket and understand how UDP works using boost library, my problem is that async_receive is not being invoked or is not getting complete in order to jump on the handler

UDP server:

#include "udp_server.h"


udp_server::udp_server(boost::asio::io_service& io_service, string bind_address, uint16_t bind_port)
    : socket_(io_service)
{
    cout << "udp_server constructor start" << endl;


    boost::shared_ptr<boost::asio::io_service::work> work(
        new boost::asio::io_service::work(io_service));

    for(int x=0; x<5; ++x)
    {
        worker_threads.create_thread(boost::bind(&udp_server::WorkerThread, this , boost::ref(io_service)));
    }

    boost::system::error_code myError;

    boost::asio::ip::address IP;
    IP = boost::asio::ip::address::from_string(bind_address, myError); 

    local_udppoint_.address(IP);
    cout << "IP Address: " << local_udppoint_.address().to_string() << endl;
    local_udppoint_.port(bind_port);
    cout << "Port: " << local_udppoint_.port() << endl;

    socket_.open(local_udppoint_.protocol(), myError);
    std::cout << "Open - " << myError.message() << std::endl;
    socket_.bind( local_udppoint_, myError );
    std::cout << "Bind - " << myError.message() << std::endl;

    udp::endpoint sender_endpoint_;

    struct test *request = (struct test *) malloc (sizeof(struct test));

    socket_.async_receive_from(
        boost::asio::buffer(&request, sizeof(request->type)), sender_endpoint_,
        boost::bind(&udp_server::handle_receive_from, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));

    cout << "udp_server constructor end" << endl;
}

void udp_server::WorkerThread(io_service &io_service_)
{
    std::cout << "Thread Start\n";
    io_service_.run();
    std::cout << "Thread Finish\n";
}

void udp_server::handle_receive_from(const boost::system::error_code& err, size_t bytes_recvd)
{
    cout << "udp_server::handle_receive_from enters?" << endl;
    if(!err)
    {
        cout << "no message" << endl;
    }
    else
    {
        cout << err.message() << endl;
    }

    if (!err && bytes_recvd > 0)
    {
        cout << "All good" << endl;
    }
    else
    {

        cout << err.message() << "2" << endl;
    }
}

udp_server::~udp_server(void)
{
    //io_service.stop();

    worker_threads.join_all();

    cout << "udp_server::destructor"  << endl;
}

Server's Main:

#include "udp_server.h"

int main()
{
  try
  {
    boost::asio::io_service io_service;
    //boost::asio::io_service::work work( io_service);
    udp_server s(io_service, "127.0.0.1", 4000);

    //io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  string a;
  cin >> a;
  return 0;

}

UDP Client:

#include "udp_client.h"


udp_client::udp_client(boost::asio::io_service& io_service, string send_address, uint16_t send_port)
    : io_service_(io_service), socket_(io_service)
{
    cout << "udp_client::constructor_start" << endl;

    boost::system::error_code myError;

    boost::asio::ip::address IP;
    IP = boost::asio::ip::address::from_string(send_address, myError); 

    remote_endpoint_.address(IP);
    cout << "IP Address: " << remote_endpoint_.address().to_string() << endl;
    remote_endpoint_.port(send_port);
    cout << "Port: " << remote_endpoint_.port() << endl;

    struct test *request = (struct test *) malloc (sizeof(struct test));

    request->type = START_STORAGE;

    socket_.async_send_to(boost::asio::buffer(&request, sizeof(request->type)), remote_endpoint_,
        boost::bind(&udp_client::start_handler, this, boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));

    cout << "udp_client::constructor_end" << endl;
}

void
udp_client::start_handler(const boost::system::error_code&, std::size_t)
{
    cout << "udp_client::start_handler()" << endl;
}

udp_client::~udp_client(void)
{
}

Client's main:

#include "udp_client.h"

int main(int argc, char* argv[])
{
  try
  {
    boost::asio::io_service io_service;

    udp_client client(io_service, "127.0.0.1", 4000);

    io_service.run ();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  string a;
  cin >> a;
  return 0;
}

As you can see in the outputs below client invoked async_send_to and the message on the handler is being printed but on the server side nothing happens

UDP Server Console output:

udp_server constructor star
Thread Start
Thread Start
Thread Start
Thread Start
Thread Start
IP Address: 127.0.0.1
Port: 4000
Open - The operation completed successfully
Bind - The operation completed successfullyudp_server constructor end
_

UDP Client Console:

udp_client::constructor_start
IP Address: 127.0.0.1
Port: 4000
udp_client::constructor_end
udp_client::start_handler()

Any ideas why async_receive_from is not completed or invoked?

1
By the way, +1 for the great formatting of your question. It's refreshing to see someone brand new (to posting) to SO posting this way.user562566

1 Answers

0
votes

Right off the bat, calling join_all on your listening threads in your destructor is going to lead to undefined behavior. You're trying to keep your server running while it's right in the middle of being destroyed. Don't do this. As an example, running the io_service from these threads, you have handlers that bind to this* that those threads will be hooking into. Inside the destructor, this* is no longer a valid object. In all of your callbacks, you should be checking the error param you were passed to see if it is set.

if(error)
{
     std::cout << "Error in MyClass::MyFunc(...): " << error << std::endl;
}

.. to get the errors printed to the console. Guaranteed you're going to see an error from ::bind that such and such is an invalid object.

You should be doing something inside your server main where you're blocking to prevent main from exiting. Move your thread group that runs your server's io_service and the io_service itself outside of the server object. Wrap the io_service with a ::work() object to prevent the io_service from stopping itself when it thinks that it's run out of work (no more connections to process).

Beyond that, the simplest thing to do is point you to the droves of TCP and UDP client and server examples that boost docs provide. http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/examples.html