TCP Sockets
TCP sockets provide reliable, ordered communication over IP network sockets and have built-in protocols for handling packet acknowledgement as well as transmission speed / bandwidth adjustment.
TCP sockets cannot be used with multicast (many to one, one to many).
The implementation supports both client-style request/response flows and
server-style bind(...) / listen(...) / accept() loops. close()
and reinit() are used by the updated teardown and reconnect paths to ensure
connection state is reset cleanly.
API Reference
Header File
Classes
-
class TcpSocket : public espp::Socket
Class for managing sending and receiving data using TCP/IP. Can be used to create client or server sockets.
TCP Client Example
espp::TcpSocket client_socket({.log_level = espp::Logger::Verbosity::WARN}); if (!client_socket.connect({.ip_address = kLoopbackAddress, .port = port})) { server_socket.close(); server_task->stop(); return fail("TCP unicast teardown", "client failed to connect"); } auto client_task = espp::Task::make_unique({ .callback = [&client_socket](std::mutex &m, std::condition_variable &cv, bool ¬ified) -> bool { static size_t iterations = 0; auto data = make_payload(24, static_cast<uint8_t>(iterations * 5)); client_socket.transmit(data); iterations++; return wait_or_stop(m, cv, notified); }, .task_config = make_task_config("TcpClient", 5 * 1024), }); client_task->start();
TCP Server Example
espp::TcpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN}); if (!server_socket.bind(port) || !server_socket.listen(kMaxConnections)) { return fail("TCP unicast teardown", "failed to bind/listen"); } auto server_task = espp::Task::make_unique({ .callback = [&server_socket, &received_messages](std::mutex &m, std::condition_variable &cv, bool ¬ified) -> bool { static std::unique_ptr<espp::TcpSocket> client_socket; if (!client_socket) { client_socket = server_socket.accept(); if (!client_socket) { return wait_or_stop(m, cv, notified, 10ms); } } ByteVector data; if (client_socket->receive(data, kMaxPacketSize)) { received_messages++; fmt::print("TCP server received {} bytes\n", data.size()); } else if (!client_socket->is_connected()) { client_socket.reset(); } return wait_or_stop(m, cv, notified, 10ms); }, .task_config = make_task_config("TcpServer"), }); server_task->start();
TCP Client Response Example
auto exchange_once = [port](uint8_t seed, ByteVector &response) -> bool { espp::TcpSocket client_socket({.log_level = espp::Logger::Verbosity::WARN}); if (!client_socket.connect({.ip_address = kLoopbackAddress, .port = port})) { return false; } auto request = make_payload(40, seed); auto transmit_config = espp::TcpSocket::TransmitConfig{ .wait_for_response = true, .response_size = kMaxPacketSize, .on_response_callback = [&response](const auto &received_response) { response = received_response; }, .response_timeout = 500ms, }; return client_socket.transmit(request, transmit_config); };
TCP Server Response Example
espp::TcpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN}); if (!server_socket.bind(port) || !server_socket.listen(kMaxConnections)) { return fail("TCP response/reconnect", "failed to bind/listen"); } auto server_task = espp::Task::make_unique({ .callback = [&server_socket, &accepted_connections]( std::mutex &m, std::condition_variable &cv, bool ¬ified) -> bool { static std::unique_ptr<espp::TcpSocket> client_socket; if (!client_socket) { client_socket = server_socket.accept(); if (client_socket) { accepted_connections++; } else { return wait_or_stop(m, cv, notified, 10ms); } } ByteVector data; if (client_socket->receive(data, kMaxPacketSize)) { client_socket->transmit(reversed(data)); } else if (!client_socket->is_connected()) { client_socket.reset(); } return wait_or_stop(m, cv, notified, 10ms); }, .task_config = make_task_config("TcpResponseServer"), }); server_task->start(); auto stop_server = [&server_socket, &server_task]() { server_socket.close(); server_task->stop(); };
Public Types
-
typedef 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 TcpSocket(const espp::TcpSocket::Config &config)
Initialize the socket and associated resources.
Note
Enables keepalive on the socket.
- Parameters:
config – Config for the socket.
-
~TcpSocket()
Tear down any resources associted with the socket.
-
void reinit()
Reinitialize the socket, cleaning it up if first it is already initalized.
-
void close()
Close the socket.
-
bool is_connected() const
Check if the socket is connected to a remote endpoint.
- Returns:
true if the socket is connected to a remote endpoint.
-
bool connect(const espp::TcpSocket::ConnectConfig &connect_config)
Open a connection to the remote TCP server.
- Parameters:
connect_config – ConnectConfig struct describing the server endpoint.
- Returns:
true if the client successfully connected to the server.
-
const espp::Socket::Info &get_remote_info() const
Get the remote endpoint info.
- Returns:
The remote endpoint info.
-
bool transmit(const std::vector<uint8_t> &data, const espp::TcpSocket::TransmitConfig &transmit_config = espp::TcpSocket::TransmitConfig::Default())
Send data to the endpoint already connected to by TcpSocket::connect. 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.
- Parameters:
data – vector of bytes to send to the remote endpoint.
transmit_config – TransmitConfig struct indicating whether to wait for a response.
- Returns:
true if the data was sent, false otherwise.
-
bool transmit(const std::vector<char> &data, const espp::TcpSocket::TransmitConfig &transmit_config = espp::TcpSocket::TransmitConfig::Default())
Send data to the endpoint already connected to by TcpSocket::connect. 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.
- Parameters:
data – vector of bytes to send to the remote endpoint.
transmit_config – TransmitConfig struct indicating whether to wait for a response.
- Returns:
true if the data was sent, false otherwise.
-
bool transmit(std::string_view data, const espp::TcpSocket::TransmitConfig &transmit_config = espp::TcpSocket::TransmitConfig::Default())
Send data to the endpoint already connected to by TcpSocket::connect. 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.
- Parameters:
data – string view of bytes to send to the remote endpoint.
transmit_config – TransmitConfig struct indicating whether to wait for a response.
- Returns:
true if the data was sent, false otherwise.
-
bool transmit(std::span<const uint8_t> data, const espp::TcpSocket::TransmitConfig &transmit_config = espp::TcpSocket::TransmitConfig::Default())
Send data to the endpoint already connected to by TcpSocket::connect. 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.
- Parameters:
data – span of bytes to send to the remote endpoint.
transmit_config – TransmitConfig struct indicating whether to wait for a response.
- Returns:
true if the data was sent, false otherwise.
-
bool receive(std::vector<uint8_t> &data, size_t max_num_bytes)
Call read on the socket, assuming it has already been configured appropriately.
- Parameters:
data – Vector of bytes of received data.
max_num_bytes – Maximum number of bytes to receive.
- Returns:
true if successfully received, false otherwise.
-
size_t receive(uint8_t *data, size_t max_num_bytes)
Call read on the socket, assuming it has already been configured appropriately.
Note
This function will block until max_num_bytes are received or the receive timeout is reached.
Note
The data pointed to by data must be at least max_num_bytes in size.
- Parameters:
data – Pointer to buffer to receive data.
max_num_bytes – Maximum number of bytes to receive.
- Returns:
Number of bytes received.
-
bool bind(int port)
Bind the socket as a server on
port.- Parameters:
port – The port to which to bind the socket.
- Returns:
true if the socket was bound.
-
bool listen(int max_pending_connections)
Listen for incoming client connections.
See also
See also
Note
Must be called after bind and before accept.
- Parameters:
max_pending_connections – Max number of allowed pending connections.
- Returns:
True if socket was able to start listening.
-
std::unique_ptr<espp::TcpSocket> accept()
Accept an incoming connection.
Note
Blocks until a connection is accepted.
Note
Must be called after listen.
Note
This function will block until a connection is accepted.
- Returns:
A unique pointer to a TcpClientSession if a connection was accepted, nullptr 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
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- 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
See also
See also
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
See also
See also
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
See also
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_fd – Socket file descriptor.
- Returns:
true if the socket file descriptor is >= 0.
-
struct TransmitConfig
Config struct for sending data to a remote TCP socket.
Note
This is only used when waiting for a response from the remote.
Public Members
-
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.
-
bool wait_for_response = false
-
typedef std::function<std::optional<std::vector<uint8_t>>(std::vector<uint8_t> &data, const Info &sender_info)> receive_callback_fn