idfxx 1.0.0
Modern C++23 components for ESP-IDF
Loading...
Searching...
No Matches
stream_socket.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Chris Leishman
3
4#pragma once
5
17#include "sdkconfig.h"
18
20#include <idfxx/net/endpoint>
21#include <idfxx/net/error>
22#include <idfxx/net/resolver>
23
24#include <chrono>
25#include <cstddef>
26#include <optional>
27#include <span>
28#include <string_view>
29
30namespace idfxx::net {
31
32class listener;
33
58class [[nodiscard]] stream_socket : public detail::ip_io_socket_base {
59public:
67 struct config {
68 address_family family = address_family::ipv4;
71 bool non_blocking = false;
73 std::optional<std::chrono::milliseconds> recv_timeout = std::nullopt;
76 std::optional<std::chrono::milliseconds> send_timeout = std::nullopt;
79 bool reuse_address = false;
82 bool no_delay = false;
85 bool keep_alive = false;
88 std::optional<std::chrono::seconds> keep_idle = std::nullopt;
91 std::optional<std::chrono::seconds> keep_interval = std::nullopt;
94 std::optional<uint32_t> keep_count = std::nullopt;
95#ifdef CONFIG_LWIP_IPV6
98 bool ipv6_only = false;
99#endif
102 std::optional<std::string_view> bind_to_device = std::nullopt;
103 };
104
105 // ----- construction (unconnected) -----
106
107#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
115
125
134 explicit stream_socket(const config& cfg);
135#endif
136
143
151
159
160 // ----- connect_to (single peer) -----
161
162#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
178 explicit stream_socket(const endpoint& peer);
179
194 explicit stream_socket(const endpoint& peer, const config& cfg);
195#endif
196
204
213
214 // ----- connect_to (resolve a host, try each candidate until one connects) -----
215
216#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
235 explicit stream_socket(std::string_view host, port_number port);
236
257 std::string_view host,
258 port_number port,
259 const config& cfg,
260 const resolver_options& opts = {}
261 );
262#endif
263
278 [[nodiscard]] static result<stream_socket> connect_to(std::string_view host, port_number port);
279
294 connect_to(std::string_view host, port_number port, const config& cfg, const resolver_options& opts = {});
295
296 // ----- connect_to_for (resolve a host with per-candidate timeout) -----
297
298#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
320 template<typename Rep, typename Period>
321 explicit stream_socket(std::string_view host, port_number port, const std::chrono::duration<Rep, Period>& timeout);
322
345 template<typename Rep, typename Period>
347 std::string_view host,
348 port_number port,
349 const std::chrono::duration<Rep, Period>& timeout,
350 const config& cfg,
351 const resolver_options& opts = {}
352 )
353 : stream_socket(idfxx::unwrap(connect_to_for(host, port, timeout, cfg, opts))) {}
354#endif
355
370 template<typename Rep, typename Period>
372 connect_to_for(std::string_view host, port_number port, const std::chrono::duration<Rep, Period>& timeout);
373
391 template<typename Rep, typename Period>
393 std::string_view host,
394 port_number port,
395 const std::chrono::duration<Rep, Period>& timeout,
396 const config& cfg,
397 const resolver_options& opts = {}
398 ) {
399 return _connect_to_for(host, port, std::chrono::ceil<std::chrono::milliseconds>(timeout), cfg, opts);
400 }
401
402 // ----- connect / disconnect -----
403
404#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
415 template<typename Rep, typename Period>
416 void connect_for(const endpoint& peer, const std::chrono::duration<Rep, Period>& timeout) {
417 idfxx::unwrap(_try_connect_for(peer, std::chrono::ceil<std::chrono::milliseconds>(timeout)));
418 }
419#endif
420
432 template<typename Rep, typename Period>
434 try_connect_for(const endpoint& peer, const std::chrono::duration<Rep, Period>& timeout) {
435 return _try_connect_for(peer, std::chrono::ceil<std::chrono::milliseconds>(timeout));
436 }
437
438 // ----- send -----
439
440#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
454 [[nodiscard]] size_t send(std::span<const std::byte> buf) { return idfxx::unwrap(try_send(buf)); }
455#endif
456
463 [[nodiscard]] result<size_t> try_send(std::span<const std::byte> buf);
464
465#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
476 [[nodiscard]] size_t send(std::string_view buf) { return idfxx::unwrap(try_send(buf)); }
477#endif
478
485 [[nodiscard]] result<size_t> try_send(std::string_view buf);
486
487#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
496 void send_all(std::span<const std::byte> buf) { idfxx::unwrap(try_send_all(buf)); }
497#endif
498
505 [[nodiscard]] result<void> try_send_all(std::span<const std::byte> buf);
506
507#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
516 void send_all(std::string_view buf) { idfxx::unwrap(try_send_all(buf)); }
517#endif
518
525 [[nodiscard]] result<void> try_send_all(std::string_view buf);
526
527 // ----- receive -----
528
529#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
538 void recv_exact(std::span<std::byte> buf) { idfxx::unwrap(try_recv_exact(buf)); }
539#endif
540
548 [[nodiscard]] result<void> try_recv_exact(std::span<std::byte> buf);
549
550 // ----- shutdown -----
551
552#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
559 void shutdown() { idfxx::unwrap(try_shutdown()); }
560#endif
561
568
569#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
583 void shutdown(direction dir) { idfxx::unwrap(try_shutdown(dir)); }
584#endif
585
598
599 // ----- TCP-only options (setters) -----
600
608 void set_no_delay(bool on) noexcept;
609
616 void set_keep_alive(bool on) noexcept;
617
625 template<typename Rep, typename Period>
626 void set_keep_idle(const std::chrono::duration<Rep, Period>& t) noexcept {
627 _set_keep_idle(std::chrono::ceil<std::chrono::seconds>(t));
628 }
629
636 template<typename Rep, typename Period>
637 void set_keep_interval(const std::chrono::duration<Rep, Period>& t) noexcept {
638 _set_keep_interval(std::chrono::ceil<std::chrono::seconds>(t));
639 }
640
648
649 // ----- TCP-only options (getters) -----
650
656
661 [[nodiscard]] bool get_keep_alive() const noexcept;
662
663private:
665
667 : ip_io_socket_base(fd, fam) {}
668
669 [[nodiscard]] result<void> _try_connect_for(const endpoint& peer, std::chrono::milliseconds timeout);
671 _make_and_connect_for(const endpoint& ep, const config& cfg, std::chrono::milliseconds timeout);
672 [[nodiscard]] static result<stream_socket> _connect_to_for(
673 std::string_view host,
674 port_number port,
675 std::chrono::milliseconds timeout,
676 const config& cfg,
678 );
679 void _set_keep_idle(std::chrono::seconds s) noexcept;
680 void _set_keep_interval(std::chrono::seconds s) noexcept;
681};
682
683// Out-of-line definitions for the bare timed `connect_to_for` overload and the
684// throwing ctor that delegates to it. These cannot be defined inside the class
685// body because they need to value-initialize `config{}`, and the nested `config`
686// class's default member initializers are not visible inside the enclosing class
687// until its definition is complete.
688
689template<typename Rep, typename Period>
691 std::string_view host,
692 port_number port,
693 const std::chrono::duration<Rep, Period>& timeout
694) {
695 return connect_to_for(host, port, timeout, config{}, {});
696}
697
698#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
699template<typename Rep, typename Period>
700stream_socket::stream_socket(std::string_view host, port_number port, const std::chrono::duration<Rep, Period>& timeout)
701 : stream_socket(idfxx::unwrap(connect_to_for(host, port, timeout))) {}
702#endif
703
704} // namespace idfxx::net
705
// end of idfxx_net_stream_socket
Address/port pair identifying a transport endpoint.
Definition endpoint.hpp:129
A TCP listening socket.
Definition listener.hpp:50
A TCP stream socket.
result< void > try_shutdown(direction dir)
Shuts down one direction on the socket (half-close).
result< void > try_send_all(std::string_view buf)
Sends a string view in full, retrying on partial sends.
void connect_for(const endpoint &peer, const std::chrono::duration< Rep, Period > &timeout)
Connects the socket with a bounded timeout.
void send_all(std::string_view buf)
Sends a string view in full, retrying on partial sends.
result< void > try_shutdown()
Shuts down both directions on the socket.
void send_all(std::span< const std::byte > buf)
Sends buf in full, retrying on partial sends.
result< void > try_connect_for(const endpoint &peer, const std::chrono::duration< Rep, Period > &timeout)
Connects the socket with a bounded timeout.
size_t send(std::string_view buf)
Sends a string view on the connected socket.
result< void > try_send_all(std::span< const std::byte > buf)
Sends buf in full, retrying on partial sends.
static result< stream_socket > make(address_family fam)
Creates a new TCP socket of the given address family.
stream_socket(const config &cfg)
Creates a new TCP socket with the given configuration.
bool get_no_delay() const noexcept
Returns whether Nagle's algorithm is currently disabled on this socket.
static result< stream_socket > connect_to(const endpoint &peer)
Creates a TCP socket and synchronously connects to the peer.
static result< stream_socket > connect_to(const endpoint &peer, const config &cfg)
Creates a TCP socket with the given configuration and connects to the peer.
stream_socket(const endpoint &peer)
Creates a TCP socket and synchronously connects to the peer.
static result< stream_socket > connect_to_for(std::string_view host, port_number port, const std::chrono::duration< Rep, Period > &timeout, const config &cfg, const resolver_options &opts={})
Resolves host : port and connects to the first reachable endpoint, bounding each per-candidate attemp...
size_t send(std::span< const std::byte > buf)
Sends bytes on the connected socket.
void shutdown(direction dir)
Shuts down one direction on the socket (half-close).
stream_socket(std::string_view host, port_number port)
Resolves host : port and connects to the first reachable endpoint, using default socket configuration...
stream_socket(std::string_view host, port_number port, const std::chrono::duration< Rep, Period > &timeout, const config &cfg, const resolver_options &opts={})
Resolves host : port and connects to the first reachable endpoint, bounding each per-candidate attemp...
stream_socket(const endpoint &peer, const config &cfg)
Creates a TCP socket with the given configuration and connects to the peer.
stream_socket(std::string_view host, port_number port, const config &cfg, const resolver_options &opts={})
Resolves host : port and connects to the first reachable endpoint.
void set_keep_idle(const std::chrono::duration< Rep, Period > &t) noexcept
Sets the time the connection must be idle before keep-alive probes start.
result< size_t > try_send(std::string_view buf)
Sends a string view on the connected socket.
static result< stream_socket > connect_to(std::string_view host, port_number port, const config &cfg, const resolver_options &opts={})
Resolves host : port and connects to the first reachable endpoint.
stream_socket(address_family fam)
Creates a new TCP socket of the given address family.
static result< stream_socket > connect_to_for(std::string_view host, port_number port, const std::chrono::duration< Rep, Period > &timeout)
Resolves host : port and connects to the first reachable endpoint, using default socket configuration...
static result< stream_socket > make(const config &cfg)
Creates a new TCP socket with the given configuration.
void set_keep_alive(bool on) noexcept
Toggles TCP keep-alive — periodic probes on idle connections to detect dead peers.
void set_keep_interval(const std::chrono::duration< Rep, Period > &t) noexcept
Sets the interval between successive keep-alive probes.
stream_socket()
Creates a new TCP socket with default configuration (IPv4, blocking).
static result< stream_socket > connect_to(std::string_view host, port_number port)
Resolves host : port and connects to the first reachable endpoint, using default socket configuration...
void set_no_delay(bool on) noexcept
Toggles whether Nagle's algorithm is disabled — when on, small writes are sent immediately rather tha...
static result< stream_socket > make()
Creates a new TCP socket with default configuration (IPv4, blocking).
result< size_t > try_send(std::span< const std::byte > buf)
Sends bytes on the connected socket.
void set_keep_count(uint32_t count) noexcept
Sets the maximum number of unacknowledged keep-alive probes before the connection is dropped.
void shutdown()
Shuts down both directions on the socket.
void recv_exact(std::span< std::byte > buf)
Receives until buf is filled, treating premature close as an error.
result< void > try_recv_exact(std::span< std::byte > buf)
Receives until buf is filled, treating premature close as an error.
address_family
Address family.
Definition endpoint.hpp:37
direction
One direction of a bidirectional transport.
Definition endpoint.hpp:70
uint16_t port_number
Port number type for transport endpoints.
Definition endpoint.hpp:60
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
Definition error.hpp:307
@ timeout
Operation timed out.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:120
Resolver hints used by the resolution functions.
Definition resolver.hpp:44
Stream socket configuration.