UDP Sockets

UDP sockets provide unreliable, unordered communication over IP network sockets.

UDP sockets can be used in unicast (point to point), multicast (one to many and many to one), and broadcast (one to all).

API Reference

Header File

Classes

class UdpSocket : public espp::Socket

Class for managing sending and receiving data using UDP/IP. Can be used to create client or server sockets.

See https://github.com/espressif/esp-idf/tree/master/examples/protocols/sockets/udp_multicast for more information on udp multicast sockets.

UDP Client Example

espp::UdpSocket client_socket({});
// create thread for sending data using the socket
auto client_task_fn = [&server_address, &client_socket, &port](auto &, auto &) {
  static size_t iterations = 0;
  std::vector<uint8_t> data{0, 1, 2, 3, 4};
  std::transform(data.begin(), data.end(), data.begin(),
                 [](const auto &d) { return d + iterations; });
  auto send_config = espp::UdpSocket::SendConfig{.ip_address = server_address, .port = port};
  client_socket.send(data, send_config);
  iterations++;
  std::this_thread::sleep_for(1s);
  // don't want to stop the task
  return false;
};
auto client_task =
    espp::Task::make_unique({.callback = client_task_fn,
                             .task_config = {.name = "Client Task", .stack_size_bytes = 5 * 1024}});
client_task->start();

UDP Server Example

    std::string server_address = "127.0.0.1";
    size_t port = 5000;
    espp::UdpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN});
    auto server_task_config = espp::Task::BaseConfig{
        .name = "UdpServer",
        .stack_size_bytes = 6 * 1024,
    };
    auto server_config = espp::UdpSocket::ReceiveConfig{
        .port = port,
        .buffer_size = 1024,
        .on_receive_callback =
            [](auto &data, auto &source) -> auto{fmt::print("Server received: {}\n"
                                                            "    from source: {}\n",
                                                            data, source);
    return std::nullopt;
  }
};
server_socket.start_receiving(server_task_config, server_config);

UDP Client Response Example

espp::UdpSocket client_socket({.log_level = espp::Logger::Verbosity::WARN});
// create threads
auto client_task_fn = [&server_address, &client_socket, &port](auto &, auto &) {
  static size_t iterations = 0;
  std::vector<uint8_t> data{0, 1, 2, 3, 4};
  std::transform(data.begin(), data.end(), data.begin(),
                 [](const auto &d) { return d + iterations; });
  auto send_config = espp::UdpSocket::SendConfig{
      .ip_address = server_address,
      .port = port,
      .wait_for_response = true,
      .response_size = 128,
      .on_response_callback = [](auto &response) { fmt::print("Client received: {}\n", response); },
  };
  // NOTE: now this call blocks until the response is received
  client_socket.send(data, send_config);
  iterations++;
  std::this_thread::sleep_for(1s);
  // don't want to stop the task
  return false;
};
auto client_task =
    espp::Task::make_unique({.callback = client_task_fn,
                             .task_config = {.name = "Client Task", .stack_size_bytes = 5 * 1024}});
client_task->start();

UDP Server Response Example

  std::string server_address = "127.0.0.1";
  size_t port = 5000;
  espp::UdpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN});
  auto server_task_config =
      espp::Task::BaseConfig{.name = "UdpServer", .stack_size_bytes = 6 * 1024};
  auto server_config = espp::UdpSocket::ReceiveConfig{
      .port = port,
      .buffer_size = 1024,
      .on_receive_callback =
          [](auto &data, auto &source) -> auto{fmt::print("Server received: {}\n"
                                                          "    from source: {}\n",
                                                          data, source);
  // reverse the data
  std::reverse(data.begin(), data.end());
  // and send it back
  return data;
}
}
;
server_socket.start_receiving(server_task_config, server_config);

UDP Multicast Client Example

espp::UdpSocket client_socket({});
// create threads
auto client_task_fn = [&client_socket, &port, &multicast_group](auto &, auto &) {
  static size_t iterations = 0;
  std::vector<uint8_t> data{0, 1, 2, 3, 4};
  std::transform(data.begin(), data.end(), data.begin(),
                 [](const auto &d) { return d + iterations; });
  auto send_config = espp::UdpSocket::SendConfig{.ip_address = multicast_group,
                                                 .port = port,
                                                 .is_multicast_endpoint = true,
                                                 .wait_for_response = true,
                                                 .response_size = 128,
                                                 .on_response_callback = [](auto &response) {
                                                   fmt::print("Client received: {}\n", response);
                                                 }};
  // NOTE: now this call blocks until the response is received
  client_socket.send(data, send_config);
  iterations++;
  std::this_thread::sleep_for(1s);
  // don't want to stop the task
  return false;
};
auto client_task =
    espp::Task::make_unique({.callback = client_task_fn,
                             .task_config = {.name = "Client Task", .stack_size_bytes = 5 * 1024}});
client_task->start();

UDP Multicast Server Example

  std::string multicast_group = "239.1.1.1";
  size_t port = 5000;
  espp::UdpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN});
  auto server_task_config = espp::Task::BaseConfig{
      .name = "UdpServer",
      .stack_size_bytes = 6 * 1024,
  };
  auto server_config = espp::UdpSocket::ReceiveConfig{
      .port = port,
      .buffer_size = 1024,
      .is_multicast_endpoint = true,
      .multicast_group = multicast_group,
      .on_receive_callback =
          [](auto &data, auto &source) -> auto{fmt::print("Server received: {}\n"
                                                          "    from source: {}\n",
                                                          data, source);
  // reverse the data
  std::reverse(data.begin(), data.end());
  // and send it back
  return data;
}
}
;
server_socket.start_receiving(server_task_config, server_config);

Public Types

std::function< std::optional< std::vector< uint8_t > >std::vector< uint8_t > &data, const Info &sender_info)> receive_callback_fn

Callback function to be called when receiving data from a client.

Param data

Byte array of data received from client

Param sender_info

Sender information (address, port)

Return

std::optional<std::vector<uint8_t>> optional data to return to sender.

typedef std::function<void(std::vector<uint8_t> &data)> response_callback_fn

Callback function to be called with data returned after transmitting data to a server.

Param data

The data that the server responded with

Public Functions

explicit UdpSocket(const Config &config)

Initialize the socket and associated resources.

Parameters

configConfig for the socket.

~UdpSocket()

Tear down any resources associted with the socket.

bool send(const std::vector<uint8_t> &data, const SendConfig &send_config)

Send data to the endpoint specified by the send_config. Can be configured to multicast (within send_config) and can be configured to block waiting for a response from the remote.

If response is requested, a callback can be provided in send_config which will be provided the response data for processing.

Note

in the case of multicast, it will block only until the first response.

Parameters
  • data – vector of bytes to send to the remote endpoint.

  • send_configSendConfig struct indicating where to send and whether to wait for a response.

Returns

true if the data was sent, false otherwise.

bool send(std::string_view data, const SendConfig &send_config)

Send data to the endpoint specified by the send_config. Can be configured to multicast (within send_config) and can be configured to block waiting for a response from the remote.

If response is requested, a callback can be provided in send_config which will be provided the response data for processing.

Note

in the case of multicast, it will block only until the first response.

Parameters
  • data – String view of bytes to send to the remote endpoint.

  • send_configSendConfig struct indicating where to send and whether to wait for a response.

Returns

true if the data was sent, false otherwise.

bool receive(size_t max_num_bytes, std::vector<uint8_t> &data, Socket::Info &remote_info)

Call recvfrom on the socket, assuming it has already been configured appropriately.

Parameters
  • max_num_bytes – Maximum number of bytes to receive.

  • data – Vector of bytes of received data.

  • remote_infoSocket::Info containing the sender’s information. This will be populated with the information about the sender.

Returns

true if successfully received, false otherwise.

bool start_receiving(Task::BaseConfig &task_config, const ReceiveConfig &receive_config)

Configure a server socket and start a thread to continuously receive and handle data coming in on that socket.

Parameters
  • task_configTask::BaseConfig struct for configuring the receive task.

  • receive_configReceiveConfig struct with socket and callback info.

Returns

true if the socket was created and task was started, false otherwise.

bool is_valid() const

Is the socket valid.

Returns

true if the socket file descriptor is >= 0.

std::optional<Info> get_ipv4_info()

Get the Socket::Info for the socket.

This will call getsockname() on the socket to get the sockaddr_storage structure, and then fill out the Socket::Info structure.

Returns

Socket::Info for the socket.

bool set_receive_timeout(const std::chrono::duration<float> &timeout)

Set the receive timeout on the provided socket.

Parameters

timeout – requested timeout, must be > 0.

Returns

true if SO_RECVTIMEO was successfully set.

bool enable_reuse()

Allow others to use this address/port combination after we’re done with it.

Returns

true if SO_REUSEADDR and SO_REUSEPORT were successfully set.

bool make_multicast(uint8_t time_to_live = 1, uint8_t loopback_enabled = true)

Configure the socket to be multicast (if time_to_live > 0). Sets the IP_MULTICAST_TTL (number of multicast hops allowed) and optionally configures whether this node should receive its own multicast packets (IP_MULTICAST_LOOP).

Parameters
  • time_to_live – number of multicast hops allowed (TTL).

  • loopback_enabled – Whether to receive our own multicast packets.

Returns

true if IP_MULTICAST_TTL and IP_MULTICAST_LOOP were set.

bool add_multicast_group(const std::string &multicast_group)

If this is a server socket, add it to the provided the multicast group.

See https://en.wikipedia.org/wiki/Multicast_address for more information.

Note

Multicast groups must be Class D addresses (224.0.0.0 to 239.255.255.255)

Parameters

multicast_group – multicast group to join.

Returns

true if IP_ADD_MEMBERSHIP was successfully set.

int select(const std::chrono::microseconds &timeout)

Select on the socket for read events.

Parameters

timeout – how long to wait for an event.

Returns

number of events that occurred.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters

rate_limit – The rate limit to use for the logger

Public Static Functions

static bool is_valid_fd(sock_type_t socket_fd)

Is the socket valid.

Parameters

socket_fdSocket file descriptor.

Returns

true if the socket file descriptor is >= 0.

struct Config

Public Members

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity level for the UDP socket logger.

struct ReceiveConfig

Public Members

size_t port

Port number to bind to / receive from.

size_t buffer_size

Max size of data we can receive at one time.

bool is_multicast_endpoint = {false}

Whether this should be a multicast endpoint.

std::string multicast_group{""}

If this is a multicast endpoint, this is the group it belongs to.

espp::Socket::receive_callback_fn on_receive_callback{nullptr}

Function containing business logic to handle data received.

struct SendConfig

Public Members

std::string ip_address

Address to send data to.

size_t port

Port number to send data to.

bool is_multicast_endpoint = {false}

Whether this should be a multicast endpoint.

bool wait_for_response = {false}

Whether to wait for a response from the remote or not.

size_t response_size{0}

If waiting for a response, this is the maximum size response we will receive.

espp::Socket::response_callback_fn on_response_callback{nullptr}

If waiting for a response, this is an optional handler which is provided the response data.

std::chrono::duration<float> response_timeout = std::chrono::duration<float>(0.5f)

If waiting for a response, this is the maximum timeout to wait.