asio C++ library

PrevUpHomeNext

Daytime.6 - An asynchronous UDP daytime server

The main() function
int main()
{
  try
  {

Create a server object to accept incoming client requests, and run the io_context object.

    asio::io_context io_context;
    udp_server server(io_context);
    io_context.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}
The udp_server class
class udp_server
{
public:

The constructor initialises a socket to listen on UDP port 13.

  udp_server(asio::io_context& io_context)
    : socket_(io_context, udp::endpoint(udp::v4(), 13))
  {
    start_receive();
  }

private:
  void start_receive()
  {

The function ip::udp::socket::async_receive_from() will cause the application to listen in the background for a new request. When such a request is received, the io_context object will invoke the handle_receive() function with two arguments: a value of type std::error_code indicating whether the operation succeeded or failed, and a size_t value bytes_transferred specifying the number of bytes received.

    socket_.async_receive_from(
        asio::buffer(recv_buffer_), remote_endpoint_,
        std::bind(&udp_server::handle_receive, this,
          asio::placeholders::error,
          asio::placeholders::bytes_transferred));
  }

The function handle_receive() will service the client request.

  void handle_receive(const std::error_code& error,
      std::size_t /*bytes_transferred*/)
  {

The error parameter contains the result of the asynchronous operation. Since we only provide the 1-byte recv_buffer_ to contain the client's request, the io_context object would return an error if the client sent anything larger. We can ignore such an error if it comes up.

    if (!error)
    {

Determine what we are going to send.

      std::shared_ptr<std::string> message(
          new std::string(make_daytime_string()));

We now call ip::udp::socket::async_send_to() to serve the data to the client.

      socket_.async_send_to(asio::buffer(*message), remote_endpoint_,
          std::bind(&udp_server::handle_send, this, message,
            asio::placeholders::error,
            asio::placeholders::bytes_transferred));

When initiating the asynchronous operation, and if using std::bind, you must specify only the arguments that match the handler's parameter list. In this program, both of the argument placeholders (asio::placeholders::error and asio::placeholders::bytes_transferred) could potentially have been removed.

Start listening for the next client request.

      start_receive();

Any further actions for this client request are now the responsibility of handle_send().

    }
  }

The function handle_send() is invoked after the service request has been completed.

  void handle_send(std::shared_ptr<std::string> /*message*/,
      const std::error_code& /*error*/,
      std::size_t /*bytes_transferred*/)
  {
  }

  udp::socket socket_;
  udp::endpoint remote_endpoint_;
  std::array<char, 1> recv_buffer_;
};

See the full source listing

Return to the tutorial index

Previous: Daytime.5 - A synchronous UDP daytime server

Next: Daytime.7 - A combined TCP/UDP asynchronous server


PrevUpHomeNext