jsocketpp 1.0
A cross-platform C++20 socket library.
Loading...
Searching...
No Matches
DatagramSocket.hpp
Go to the documentation of this file.
1
8
9#pragma once
10
11#include "BufferView.hpp"
12#include "common.hpp"
13#include "DatagramPacket.hpp"
15#include "SocketOptions.hpp"
16
17#include <atomic>
18#include <bit>
19#include <optional>
20#include <span>
21#include <string>
22
23namespace jsocketpp
24{
25
64enum class DatagramReceiveMode : std::uint8_t
65{
79
98
114};
115
136{
144
152 bool allowGrow = true;
153
161 bool allowShrink = true;
162
172 int recvFlags = 0;
173
181 bool updateLastRemote = true;
182
190 bool resolveNumeric = true;
191
220 bool errorOnTruncate{true};
221};
222
249{
259 std::size_t bytes = 0;
260
271 std::size_t datagramSize = 0;
272
282 bool truncated = false;
283
294 sockaddr_storage src{};
295
305 socklen_t srcLen = 0;
306};
307
327{
340
351 bool requireExact = true;
352
363 bool padIfSmaller = false;
364
374 bool errorOnTruncate = true;
375
387 bool autoResizeDynamic = true;
388};
389
412enum class Direction : std::uint8_t
413{
417};
418
484{
485 public:
555 explicit DatagramSocket(Port localPort = 0, std::string_view localAddress = "",
556 std::optional<std::size_t> recvBufferSize = std::nullopt,
557 std::optional<std::size_t> sendBufferSize = std::nullopt,
558 std::optional<std::size_t> internalBufferSize = std::nullopt, bool reuseAddress = true,
559 int soRecvTimeoutMillis = -1, int soSendTimeoutMillis = -1, bool nonBlocking = false,
560 bool dualStack = true, bool autoBind = true, bool autoConnect = false,
561 std::string_view remoteAddress = "", Port remotePort = 0, int connectTimeoutMillis = -1);
562
579 ~DatagramSocket() noexcept override;
580
608
635
666 : SocketOptions(rhs.getSocketFd()), _remoteAddr(rhs._remoteAddr), _remoteAddrLen(rhs._remoteAddrLen),
667 _haveRemoteAddr(rhs._haveRemoteAddr.load(std::memory_order_relaxed)), _localAddr(rhs._localAddr),
668 _localAddrLen(rhs._localAddrLen), _haveLocalAddr(rhs._haveLocalAddr.load(std::memory_order_relaxed)),
669 _internalBuffer(std::move(rhs._internalBuffer)), _port(rhs._port), _isBound(rhs._isBound),
670 _isConnected(rhs._isConnected)
671 {
672 rhs.cleanup();
673 }
674
714 {
715 if (this != &rhs)
716 {
717 // Clean up current socket
718 try
719 {
720 close(); // Clean up existing resources
721 }
722 catch (...)
723 {
724 // CloseSocket error is ignored.
725 // TODO: Consider adding an internal flag, nested exception, or user-configurable error handler
726 // to report errors in future versions.
727 }
728
729 // Transfer ownership
730 setSocketFd(rhs.getSocketFd());
731 _remoteAddr = rhs._remoteAddr;
732 _remoteAddrLen = rhs._remoteAddrLen;
733 _haveRemoteAddr.store(rhs._haveRemoteAddr.load(std::memory_order_relaxed));
734 _localAddr = rhs._localAddr;
735 _localAddrLen = rhs._localAddrLen;
736 _haveLocalAddr.store(rhs._haveLocalAddr.load(std::memory_order_relaxed));
737 _internalBuffer = std::move(rhs._internalBuffer);
738 _port = rhs._port;
739 _isBound = rhs._isBound;
740 _isConnected = rhs._isConnected;
741
742 // Reset source
743 rhs.cleanup();
744 }
745 return *this;
746 }
747
778 void bind();
779
812 void bind(Port localPort);
813
849 void bind(std::string_view localAddress, Port localPort);
850
880 [[nodiscard]] bool isBound() const noexcept { return _isBound; }
881
945 void connect(std::string_view host, Port port, int timeoutMillis);
946
985 void disconnect();
986
1016 [[nodiscard]] bool isConnected() const noexcept { return _isConnected; }
1017
1063 [[nodiscard]] std::string getLocalIp(bool convertIPv4Mapped);
1064
1099 [[nodiscard]] Port getLocalPort();
1100
1144 [[nodiscard]] std::string getLocalSocketAddress(bool convertIPv4Mapped);
1145
1182 [[nodiscard]] std::string getRemoteIp(bool convertIPv4Mapped) const;
1183
1215 [[nodiscard]] Port getRemotePort() const;
1216
1256 [[nodiscard]] std::string getRemoteSocketAddress(bool convertIPv4Mapped) const;
1257
1318 void write(std::string_view message) const;
1319
1383 template <typename T> void write(const T& value) const
1384 {
1385 static_assert(std::is_trivially_copyable_v<T>, "DatagramSocket::write<T>() requires trivially copyable type");
1386 static_assert(std::is_standard_layout_v<T>, "DatagramSocket::write<T>() requires standard layout type");
1387
1388 if (getSocketFd() == INVALID_SOCKET)
1389 throw SocketException("DatagramSocket::write<T>(): socket is not open.");
1390
1391 if (!_isConnected)
1392 throw SocketException("DatagramSocket::write<T>(): socket is not connected. Use writeTo() instead.");
1393
1394 enforceSendCapConnected(sizeof(T));
1395
1396 const auto buffer = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
1397 internal::sendExact(getSocketFd(), buffer.data(), buffer.size());
1398 }
1399
1461 void write(std::span<const std::byte> data) const;
1462
1503 void writeAll(std::string_view message) const;
1504
1535 template <typename T> void writePrefixed(const std::string_view payload) const
1536 {
1538 }
1539
1565 template <typename T> void writePrefixed(const std::span<const std::byte> payload) const
1566 {
1567 sendPrefixedConnected<T>(payload);
1568 }
1569
1600 template <typename T>
1601 void writePrefixedTo(const std::string_view host, const Port port, const std::string_view payload) const
1602 {
1603 sendPrefixedUnconnected<T>(host, port, asBytes(payload));
1604 }
1605
1632 template <typename T>
1633 void writePrefixedTo(const std::string_view host, const Port port, const std::span<const std::byte> payload) const
1634 {
1635 sendPrefixedUnconnected<T>(host, port, payload);
1636 }
1637
1692 void writev(std::span<const std::string_view> buffers) const;
1693
1750 void writevAll(std::span<const std::string_view> buffers) const;
1751
1816 void writeFrom(const void* data, std::size_t len) const;
1817
1882 void writeWithTimeout(std::string_view data, int timeoutMillis) const;
1883
1943 void write(const DatagramPacket& packet);
1944
2014 void writeTo(std::string_view host, Port port, std::string_view message);
2015
2089 void writeTo(std::string_view host, Port port, std::span<const std::byte> data);
2090
2169 template <typename T> void writeTo(const std::string_view host, const Port port, const T& value) const
2170 {
2171 static_assert(std::is_trivially_copyable_v<T>, "DatagramSocket::writeTo<T>() requires trivially copyable type");
2172 static_assert(std::is_standard_layout_v<T>, "DatagramSocket::writeTo<T>() requires standard layout type");
2173
2174 if (getSocketFd() == INVALID_SOCKET)
2175 throw SocketException("DatagramSocket::writeTo<T>(): socket is not open.");
2176
2177 if constexpr (sizeof(T) == 0)
2178 return;
2179
2180 const auto buf = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
2181 sendUnconnectedTo(host, port, buf.data(), buf.size());
2182 }
2183
2255 DatagramReadResult read(DatagramPacket& packet, const DatagramReadOptions& opts) const;
2256
2314 [[nodiscard]] DatagramReadResult readInto(void* buffer, std::size_t len,
2315 const DatagramReadOptions& opts = {}) const;
2316
2371 [[nodiscard]] DatagramReadResult readInto(std::span<char> out, const DatagramReadOptions& opts = {}) const;
2372
2425 template <typename T, std::enable_if_t<detail::is_dynamic_buffer_v<T>, int> = 0>
2426 [[nodiscard]] T read(const DatagramReadOptions& opts = {},
2427 std::size_t minCapacity = DefaultDatagramReceiveSize) const
2428 {
2429 using Ptr = decltype(std::declval<T&>().data());
2430 using Elem = std::remove_pointer_t<Ptr>;
2431 static_assert(std::is_same_v<Elem, char> || std::is_same_v<Elem, unsigned char> ||
2432 std::is_same_v<Elem, std::byte>,
2433 "T must be a contiguous byte-like container of char, unsigned char, or std::byte");
2434
2435 if (minCapacity == 0)
2436 minCapacity = 1; // allow clean reception of zero-length datagrams
2437
2438 std::size_t capacity = (std::min) (minCapacity, MaxDatagramPayloadSafe);
2439
2440 // Prefer an exact allocation when we can learn the size (either explicitly requested,
2441 // or because strict no-truncation is desired).
2442 std::size_t probed = 0;
2443 const bool wantPreflight = (opts.mode != DatagramReceiveMode::NoPreflight) || opts.errorOnTruncate;
2444
2445 if (wantPreflight)
2446 {
2447 try
2448 {
2449 if (const std::size_t exact = internal::nextDatagramSize(getSocketFd()); exact > 0)
2450 {
2451 probed = (std::min) (exact, MaxDatagramPayloadSafe);
2452 capacity = (std::max) (capacity, probed); // grow to fit the datagram exactly
2453 }
2454 }
2455 catch (const SocketException&)
2456 {
2457 // graceful degradation: keep current capacity
2458 }
2459 }
2460
2461 T out;
2462 out.resize(capacity);
2463
2464 DatagramReadOptions local = opts;
2465 if (probed > 0)
2466 local.mode = DatagramReceiveMode::NoPreflight; // avoid redundant preflight on the actual recv
2467
2468 // Use the policy-aware backbone; it will enforce errorOnTruncate as documented.
2469 if (auto res = readInto(reinterpret_cast<void*>(out.data()), out.size(), local); res.bytes < out.size())
2470 out.resize(res.bytes); // shrink to actual bytes received (no null terminator implied)
2471
2472 return out;
2473 }
2474
2523 template <typename T, std::enable_if_t<detail::is_fixed_buffer_v<T>, int> = 0>
2524 [[nodiscard]] T read(const DatagramReadOptions& opts = {}) const
2525 {
2526 using Ptr = decltype(std::declval<T&>().data());
2527 using Elem = std::remove_pointer_t<Ptr>;
2528 static_assert(std::is_same_v<Elem, char> || std::is_same_v<Elem, unsigned char> ||
2529 std::is_same_v<Elem, std::byte>,
2530 "T must be a contiguous byte-like container of char, unsigned char, or std::byte");
2531 static_assert(sizeof(Elem) == 1, "T element type must be 1 byte wide");
2532
2533 T out{}; // value-init so any unused tail is predictable (typically zero)
2534 if (out.size() == 0)
2535 return out;
2536
2537 // Zero-allocation, policy-aware read
2538 (void) readInto(std::span<char>(reinterpret_cast<char*>(out.data()), out.size()), opts);
2539 return out;
2540 }
2541
2614 template <typename T>
2615 [[nodiscard]] DatagramReadResult readFrom(T& buffer, std::string* senderAddr, Port* senderPort,
2616 const DatagramReadOptions& opts) const
2617 {
2618 if (getSocketFd() == INVALID_SOCKET)
2619 throw SocketException("DatagramSocket::readFrom(): socket is not open.");
2620
2621 DatagramReadResult result{};
2622
2623 // Determine current capacity and whether we can grow.
2624 constexpr bool isDynamic = detail::is_dynamic_buffer_v<T>;
2625 constexpr bool isFixed = detail::is_fixed_buffer_v<T>;
2626 static_assert(isDynamic || isFixed, "T must be a supported dynamic or fixed-size contiguous byte container");
2627
2628 auto currSize = static_cast<std::size_t>(buffer.size());
2629
2630 // For dynamic containers, ensure at least 1 byte to properly consume zero-length datagrams.
2631 if constexpr (isDynamic)
2632 {
2633 if (currSize == 0)
2634 currSize = (std::min) (DefaultDatagramReceiveSize, MaxDatagramPayloadSafe);
2635 }
2636
2637 // Optional preflight: if we can learn the size, either grow (dynamic) or enforce policy (fixed).
2638 std::size_t probed = 0;
2639 const bool wantPreflight = (opts.mode != DatagramReceiveMode::NoPreflight) || opts.errorOnTruncate;
2640
2641 if (wantPreflight)
2642 {
2643 try
2644 {
2645 const std::size_t exact = internal::nextDatagramSize(getSocketFd());
2646 if (exact > 0)
2647 {
2648 probed = (std::min) (exact, MaxDatagramPayloadSafe);
2649
2650 if constexpr (isDynamic)
2651 {
2652 if (probed > currSize)
2653 currSize = probed; // grow to fit the datagram exactly
2654 }
2655 else
2656 { // fixed
2657 if (opts.errorOnTruncate && probed > currSize)
2658 {
2659 // Early, non-destructive failure: datagram remains queued.
2660 throw SocketException("DatagramSocket::readFrom(): fixed buffer too small for incoming "
2661 "datagram (preflight).");
2662 }
2663 }
2664 }
2665 }
2666 catch (const SocketException&)
2667 {
2668 // graceful degradation; fall back to single-recv path
2669 probed = 0;
2670 }
2671 }
2672
2673 // Clamp to safety cap.
2674 currSize = (std::min) (currSize, MaxDatagramPayloadSafe);
2675
2676 // Resize dynamic container as needed.
2677 if constexpr (isDynamic)
2678 buffer.resize(currSize);
2679
2680 // Perform exactly one receive using the low-level backbone to obtain sender info.
2681 sockaddr_storage src{};
2682 auto srcLen = static_cast<socklen_t>(sizeof(src));
2683 std::size_t datagramSize = 0;
2684 bool truncated = false;
2685
2686 const DatagramReceiveMode effectiveMode = (probed > 0 ? DatagramReceiveMode::NoPreflight : opts.mode);
2687
2688 const std::size_t n = readIntoBuffer(reinterpret_cast<char*>(buffer.data()), currSize, effectiveMode,
2689 opts.recvFlags, &src, &srcLen, &datagramSize, &truncated);
2690
2691 // Enforce strict no-truncation after the read if we couldn't fail early.
2692 if (opts.errorOnTruncate && (truncated || (datagramSize > 0 && datagramSize > currSize)))
2693 {
2694 throw SocketException("DatagramSocket::readFrom(): datagram truncated into destination buffer.");
2695 }
2696
2697 // Shrink dynamic container to the actual bytes received.
2698 if constexpr (isDynamic)
2699 {
2700 if (n < buffer.size())
2701 buffer.resize(n);
2702 }
2703
2704 // Fill the result structure.
2705 result.bytes = n;
2706 result.datagramSize = (datagramSize > 0 ? datagramSize : n);
2707 result.truncated = truncated;
2708 result.src = src;
2709 result.srcLen = srcLen;
2710
2711 // Optional: update last-remote if requested by options.
2712 if (opts.updateLastRemote)
2713 rememberRemote(src, srcLen);
2714
2715 // Resolve numeric sender info if requested.
2716 if ((senderAddr || senderPort) && srcLen > 0)
2717 {
2718 std::string addrStr;
2719 Port portNum{};
2720 internal::resolveNumericHostPort(reinterpret_cast<const sockaddr*>(&src), srcLen, addrStr, portNum);
2721 if (senderAddr)
2722 *senderAddr = std::move(addrStr);
2723 if (senderPort)
2724 *senderPort = portNum;
2725 }
2726
2727 return result;
2728 }
2729
2772 [[nodiscard]] DatagramReadResult readExact(void* buffer, std::size_t exactLen,
2773 const ReadExactOptions& opts = {}) const;
2774
2785 [[nodiscard]] DatagramReadResult readExact(std::span<char> out, const ReadExactOptions& opts = {}) const;
2786
2860 template <typename T>
2861 [[nodiscard]] DatagramReadResult readExact(T& buffer, const std::size_t exactLen,
2862 const ReadExactOptions& opts = {}) const
2863 {
2864 if (getSocketFd() == INVALID_SOCKET)
2865 throw SocketException("DatagramSocket::readExact(T&,size_t): socket is not open.");
2866 if (exactLen == 0)
2867 throw SocketException("DatagramSocket::readExact(T&,size_t): exactLen must be > 0.");
2868
2869 // Validate container element type and contiguity at compile time
2870 using Ptr = decltype(std::declval<T&>().data());
2871 using Elem = std::remove_pointer_t<Ptr>;
2872 static_assert(sizeof(Elem) == 1, "T element type must be 1 byte wide");
2873 static_assert(std::is_same_v<Elem, char> || std::is_same_v<Elem, unsigned char> ||
2874 std::is_same_v<Elem, std::byte>,
2875 "T must be a contiguous byte-like container of char, unsigned char, or std::byte");
2876
2877 DatagramReadResult res{};
2878
2879 if constexpr (detail::is_dynamic_buffer_v<T>)
2880 {
2881 // Ensure capacity matches policy
2882 if (opts.autoResizeDynamic)
2883 {
2884 if (exactLen > MaxDatagramPayloadSafe)
2885 throw SocketException(
2886 "DatagramSocket::readExact(T&,size_t): exactLen exceeds MaxDatagramPayloadSafe.");
2887 buffer.resize(exactLen); // value-initializes any new bytes
2888 }
2889 else
2890 {
2891 if (buffer.size() < exactLen)
2892 throw SocketException(
2893 "DatagramSocket::readExact(T&,size_t): buffer too small and autoResizeDynamic=false.");
2894 }
2895
2896 // Perform policy-aware exact read into the first exactLen bytes
2897 res = readExact(std::span(reinterpret_cast<char*>(buffer.data()), exactLen), opts);
2898
2899 // Normalize container size to the logical exact length (keeps API guarantees)
2900 if (buffer.size() != exactLen)
2901 buffer.resize(exactLen); // any extension is value-initialized (zeros)
2902 }
2903 else if constexpr (detail::is_fixed_buffer_v<T>)
2904 {
2905 if (buffer.size() < exactLen)
2906 throw SocketException("DatagramSocket::readExact(T&,size_t): fixed buffer smaller than exactLen.");
2907
2908 // Exact read directly into the fixed-size storage (only the first exactLen bytes are touched)
2909 res = readExact(std::span(reinterpret_cast<char*>(buffer.data()), exactLen), opts);
2910 // Size is fixed; if padding was requested, the span path already zero-filled the tail.
2911 }
2912 else
2913 {
2915 "T must be a supported dynamic or fixed-size contiguous byte container");
2916 }
2917
2918 return res;
2919 }
2920
2973 std::size_t readAtMost(std::span<char> out, const DatagramReadOptions& opts = {}) const;
2974
3025 [[nodiscard]] std::string readAtMost(std::size_t n) const;
3026
3076 [[nodiscard]] std::string readAvailable() const;
3077
3136 std::size_t readAvailable(std::span<char> out, const DatagramReadOptions& opts = {}) const;
3137
3183 std::size_t readIntoExact(void* buffer, std::size_t len) const;
3184
3222 [[nodiscard]] std::string readAtMostWithTimeout(std::size_t n, int timeoutMillis) const;
3223
3271 template <
3272 typename T,
3273 std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T> && std::is_trivially_copyable_v<T>, int> = 0>
3274 [[nodiscard]] std::string readPrefixed(std::size_t maxPayloadLen = MaxDatagramPayloadSafe,
3275 const std::endian prefixEndian = std::endian::big) const
3276 {
3277 if (getSocketFd() == INVALID_SOCKET)
3278 throw SocketException("DatagramSocket::readPrefixed<T>(): socket is not open.");
3279
3280 // Clamp guard values
3281 if (maxPayloadLen == 0)
3282 {
3283 // Only valid if the incoming prefix is exactly zero; we’ll validate after the read.
3284 // Keep as 0 here; we still read the datagram and then enforce.
3285 }
3286 constexpr std::size_t safeMax = MaxDatagramPayloadSafe;
3287 if (maxPayloadLen > safeMax)
3288 maxPayloadLen = safeMax;
3289
3290 // Prefer allocating exactly the datagram size when we can probe.
3291 std::size_t probed = 0;
3292 try
3293 {
3294 if (const std::size_t sz = internal::nextDatagramSize(getSocketFd()); sz > 0)
3295 probed = (std::min) (sz, safeMax);
3296 }
3297 catch (const SocketException&)
3298 {
3299 // Fallback to safe maximum below.
3300 probed = 0;
3301 }
3302
3303 const std::size_t cap = (probed > 0) ? probed : safeMax;
3304 if (cap < sizeof(T))
3305 {
3306 // We’ll still read and then fail with a precise error if needed.
3307 }
3308
3309 std::string frame(cap, '\0');
3310
3311 // Read the entire datagram (no truncation); a single recv is used internally.
3313 ro.errorOnTruncate = true; // enforce full-datagram semantics
3314 const std::size_t n = readAvailable(std::span<char>(frame.data(), frame.size()), ro);
3315 frame.resize(n);
3316
3317 // Validate minimal size (must contain the prefix)
3318 if (frame.size() < sizeof(T))
3319 throw SocketException("DatagramSocket::readPrefixed<T>(): datagram smaller than length prefix.");
3320
3321 // Decode prefix in requested endian
3322 const auto* p = reinterpret_cast<const unsigned char*>(frame.data());
3323 std::uint64_t decl = 0;
3324
3325 if (prefixEndian == std::endian::big)
3326 {
3327 for (std::size_t i = 0; i < sizeof(T); ++i)
3328 decl = (decl << 8) | static_cast<std::uint64_t>(p[i]);
3329 }
3330 else
3331 { // little-endian
3332 for (std::size_t i = 0; i < sizeof(T); ++i)
3333 decl |= (static_cast<std::uint64_t>(p[i]) << (8 * i));
3334 }
3335
3336 const std::size_t declared = decl;
3337
3338 // Enforce declared size == actual payload length
3339 if (const std::size_t actual = frame.size() - sizeof(T); declared != actual)
3340 throw SocketException("DatagramSocket::readPrefixed<T>(): prefix/payload size mismatch.");
3341
3342 // Enforce application-level max payload guard
3343 if (declared > maxPayloadLen)
3344 throw SocketException("DatagramSocket::readPrefixed<T>(): payload exceeds maxPayloadLen.");
3345
3346 // Return payload slice
3347 return {frame.data() + sizeof(T), frame.data() + sizeof(T) + declared};
3348 }
3349
3380 void discard(const DatagramReadOptions& opts = {}) const;
3381
3423 void discardExact(std::size_t n, const DatagramReadOptions& opts = {}) const;
3424
3459 [[nodiscard]] DatagramReadResult readv(std::span<BufferView> buffers, const DatagramReadOptions& opts = {}) const;
3460
3510 [[nodiscard]] DatagramReadResult readvAll(std::span<BufferView> buffers,
3511 const DatagramReadOptions& opts = {}) const;
3512
3522 std::size_t readvAllBytes(std::span<BufferView> buffers, const DatagramReadOptions& opts = {}) const
3523 {
3524 return readvAll(buffers, opts).bytes;
3525 }
3526
3563 [[nodiscard]] DatagramReadResult readvAtMostWithTimeout(std::span<BufferView> buffers, int timeoutMillis,
3564 const DatagramReadOptions& opts = {}) const;
3565
3571 std::size_t readvAtMostWithTimeout(std::span<BufferView> buffers, int timeoutMillis) const
3572 {
3573 return readvAtMostWithTimeout(buffers, timeoutMillis, DatagramReadOptions{}).bytes;
3574 }
3575
3631 [[nodiscard]] DatagramReadResult readvAllWithTotalTimeout(std::span<BufferView> buffers, int totalTimeoutMillis,
3632 const DatagramReadOptions& opts = {}) const;
3633
3639 std::size_t readvAllWithTotalTimeoutBytes(std::span<BufferView> buffers, int totalTimeoutMillis,
3640 const DatagramReadOptions& opts = {}) const
3641 {
3642 return readvAllWithTotalTimeout(buffers, totalTimeoutMillis, opts).bytes;
3643 }
3644
3692 [[nodiscard]] DatagramReadResult peek(DatagramPacket& packet, bool allowResize = true,
3693 const DatagramReadOptions& opts = {}) const;
3694
3724 bool hasPendingData(int timeoutMillis) const;
3725
3780 [[nodiscard]] std::optional<int> getMTU() const;
3781
3817 void waitReady(Direction dir, int timeoutMillis) const;
3818
3864 void setInternalBufferSize(std::size_t newLen);
3865
3916 void close();
3917
3955 [[nodiscard]] bool isValid() const noexcept { return getSocketFd() != INVALID_SOCKET; }
3956
3988 [[nodiscard]] bool isClosed() const noexcept { return getSocketFd() == INVALID_SOCKET; }
3989
4043 [[nodiscard]] std::optional<std::pair<sockaddr_storage, socklen_t>> getLastPeerSockAddr() const
4044 {
4045 if (!_haveRemoteAddr)
4046 {
4047 // No communication has occurred; no peer info available
4048 return std::nullopt;
4049 }
4050
4051 return std::make_pair(_remoteAddr, _remoteAddrLen);
4052 }
4053
4054 protected:
4110 std::size_t readIntoBuffer(char* buf, std::size_t len, DatagramReceiveMode mode, int recvFlags,
4111 sockaddr_storage* outSrc, socklen_t* outSrcLen, std::size_t* outDatagramSz,
4112 bool* outTruncated) const;
4113
4146 void cleanup();
4147
4189 void cleanupAndThrow(int errorCode);
4190
4237 void cleanupAndRethrow();
4238
4270 std::size_t chooseReceiveSize() const
4271 {
4272 if (const std::size_t exact = internal::nextDatagramSize(getSocketFd()); exact > 0)
4273 return (exact > MaxDatagramPayloadSafe) ? MaxDatagramPayloadSafe : exact;
4274
4275 const std::size_t fallback = _internalBuffer.empty() ? DefaultDatagramReceiveSize : _internalBuffer.size();
4276
4277 return (fallback > MaxDatagramPayloadSafe) ? MaxDatagramPayloadSafe : fallback;
4278 }
4279
4294 void rememberRemote(const sockaddr_storage& src, const socklen_t len) const noexcept
4295 {
4296 _remoteAddr = src;
4297 _remoteAddrLen = len;
4298 _haveRemoteAddr.store(true, std::memory_order_relaxed);
4299 }
4300
4343 static void throwSizeMismatch(const std::size_t expected, const std::size_t actual, const bool isProbedKnown)
4344 {
4345 // Two-arg pattern with wrapped message for consistency across the project
4346 constexpr int err = 0; // logical (not a system error)
4347 const std::string msg =
4348 "UDP datagram size mismatch: expected " + std::to_string(expected) +
4349 (isProbedKnown ? (", probed " + std::to_string(actual)) : (", received " + std::to_string(actual)));
4350 throw SocketException(err, msg);
4351 }
4352
4407 void enforceSendCapConnected(const std::size_t payloadSize) const
4408 {
4409 // Zero-length datagrams are valid (often used as keep-alives); nothing to enforce.
4410 if (payloadSize == 0)
4411 return;
4412
4413 if (getSocketFd() == INVALID_SOCKET)
4414 throw SocketException(0, "DatagramSocket::enforceSendCapConnected(): socket is not open.");
4415
4416 if (!_isConnected)
4417 throw SocketException(0, "DatagramSocket::enforceSendCapConnected(): socket is not connected.");
4418
4419 // Determine remote family: prefer cached last-peer, else query getpeername().
4420 int family = AF_UNSPEC;
4421 if (_remoteAddrLen > 0)
4422 {
4423 family = reinterpret_cast<const sockaddr*>(&_remoteAddr)->sa_family;
4424 }
4425 else
4426 {
4427 sockaddr_storage peer{};
4428 auto len = static_cast<socklen_t>(sizeof(peer));
4429 if (::getpeername(getSocketFd(), reinterpret_cast<sockaddr*>(&peer), &len) == SOCKET_ERROR)
4430 {
4431 const int err = GetSocketError();
4432 throw SocketException(err, SocketErrorMessage(err));
4433 }
4434 family = reinterpret_cast<const sockaddr*>(&peer)->sa_family;
4435 }
4436
4437 // Always allow the conservative, cross-stack "safe" ceiling.
4438 if (payloadSize <= MaxDatagramPayloadSafe)
4439 return;
4440
4441 // If the connected peer is IPv6, allow the IPv6 theoretical headroom.
4442 if (family == AF_INET6 && payloadSize <= MaxUdpPayloadIPv6)
4443 return;
4444
4445 // Compose a precise, helpful error message and throw a logical (non-OS) error.
4446 std::string msg = "UDP datagram payload (" + std::to_string(payloadSize) + " bytes) ";
4447
4448 if (payloadSize > MaxUdpPayloadIPv6)
4449 {
4450 msg += "exceeds IPv6 theoretical maximum (" + std::to_string(MaxUdpPayloadIPv6) + "). ";
4451 }
4452 else if (family == AF_INET)
4453 {
4454 msg += "exceeds IPv4 maximum (" + std::to_string(MaxUdpPayloadIPv4) + "). ";
4455 }
4456 else
4457 {
4458 // AF_UNSPEC or other: fell through the safe ceiling and not eligible for IPv6 headroom.
4459 msg += "exceeds MaxDatagramPayloadSafe (" + std::to_string(MaxDatagramPayloadSafe) + "). ";
4460 }
4461
4462 msg += "Split the payload across multiple datagrams or switch to a stream protocol.";
4463
4464 // Two-arg form for consistency with other logical throws in the project.
4465 throw SocketException(0, msg);
4466 }
4467
4503 void sendUnconnectedTo(std::string_view host, Port port, const void* data, std::size_t len);
4504
4544 static std::span<const std::byte> asBytes(const std::string_view sv) noexcept
4545 {
4546 return {reinterpret_cast<const std::byte*>(sv.data()), sv.size()};
4547 }
4548
4592 template <typename T> static std::array<std::byte, sizeof(T)> encodeLengthPrefixBE(std::size_t n)
4593 {
4594 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
4595 "T must be an unsigned integral type for the length prefix.");
4596 if (n > static_cast<std::size_t>((std::numeric_limits<T>::max)()))
4597 throw SocketException("writePrefixed<T>(): payload too large for prefix type T.");
4598 std::array<std::byte, sizeof(T)> out{};
4599 T v = static_cast<T>(n);
4600 for (std::size_t i = 0; i < sizeof(T); ++i)
4601 {
4602 out[sizeof(T) - 1 - i] = static_cast<std::byte>(v & static_cast<T>(0xFF));
4603 v = static_cast<T>(v >> 8);
4604 }
4605 return out;
4606 }
4607
4668 template <typename T> void sendPrefixedConnected(const std::span<const std::byte> payload) const
4669 {
4670 if (getSocketFd() == INVALID_SOCKET)
4671 throw SocketException("DatagramSocket::sendPrefixedConnected<T>(): socket is not open.");
4672 if (!isConnected())
4673 throw SocketException(
4674 "DatagramSocket::sendPrefixedConnected<T>(): socket is not connected. Use write*To().");
4675
4676 const std::size_t n = payload.size();
4677 const auto prefix = encodeLengthPrefixBE<T>(n);
4678 const std::size_t total = sizeof(T) + n;
4679
4681
4682 // Coalesce [prefix | payload] into one datagram and send once.
4683 std::vector<std::byte> datagram(total);
4684 std::memcpy(datagram.data(), prefix.data(), sizeof(T));
4685 if (n != 0)
4686 {
4687 std::memcpy(datagram.data() + sizeof(T), payload.data(), n);
4688 }
4689 internal::sendExact(getSocketFd(), datagram.data(), datagram.size());
4690 }
4691
4751 template <typename T>
4752 void sendPrefixedUnconnected(const std::string_view host, const Port port, const std::span<const std::byte> payload)
4753 {
4754 if (getSocketFd() == INVALID_SOCKET)
4755 throw SocketException("DatagramSocket::sendPrefixedUnconnected<T>(): socket is not open.");
4756
4757 const std::size_t n = payload.size();
4758 const auto prefix = encodeLengthPrefixBE<T>(n);
4759 const std::size_t total = sizeof(T) + n;
4760
4761 // Build once; family skipping & send are handled inside sendUnconnectedTo().
4762 std::vector<std::byte> datagram(total);
4763 std::memcpy(datagram.data(), prefix.data(), sizeof(T));
4764 if (n != 0)
4765 {
4766 std::memcpy(datagram.data() + sizeof(T), payload.data(), n);
4767 }
4768 sendUnconnectedTo(host, port, datagram.data(), datagram.size());
4769 }
4770
4789 void cacheLocalEndpoint() noexcept
4790 {
4791 sockaddr_storage ss{};
4792 auto len = static_cast<socklen_t>(sizeof(ss));
4793#if defined(_WIN32)
4794 if (::getsockname(getSocketFd(), reinterpret_cast<sockaddr*>(&ss), &len) == SOCKET_ERROR)
4795 return;
4796#else
4797 if (::getsockname(getSocketFd(), reinterpret_cast<sockaddr*>(&ss), &len) == -1)
4798 return;
4799#endif
4800 _localAddr = ss;
4801 _localAddrLen = len;
4802 _haveLocalAddr.store(true, std::memory_order_relaxed); // or set your existing flag
4803 }
4804
4850 [[nodiscard]] bool tryGetRemoteSockaddr(sockaddr_storage& out, socklen_t& outLen) const;
4851
4852 private:
4853 mutable sockaddr_storage
4855 mutable socklen_t _remoteAddrLen =
4856 0;
4857 mutable std::atomic_bool _haveRemoteAddr = false;
4858 sockaddr_storage _localAddr{};
4859 socklen_t _localAddrLen = 0;
4860 std::atomic_bool _haveLocalAddr =
4861 false;
4862 std::vector<char> _internalBuffer;
4864 bool _isBound = false;
4865 bool _isConnected = false;
4866};
4867
4868} // namespace jsocketpp
Represents a raw writable memory region for scatter/gather I/O.
UDP datagram packet class for jsocketpp.
Defines the SocketOptions base class for cross-platform socket option access.
Type traits and utilities for detecting and validating buffer types.
Represents a UDP datagram packet, encapsulating both payload and addressing information.
Definition DatagramPacket.hpp:48
std::vector< char > _internalBuffer
Internal buffer for read operations.
Definition DatagramSocket.hpp:4862
socklen_t _localAddrLen
Size in bytes of the cached local address stored in _localAddr.
Definition DatagramSocket.hpp:4859
sockaddr_storage _localAddr
Cached local socket address (set by bind()/UDP connect() via getsockname()).
Definition DatagramSocket.hpp:4858
bool _isBound
True if the socket is bound to an address.
Definition DatagramSocket.hpp:4864
void rememberRemote(const sockaddr_storage &src, const socklen_t len) const noexcept
Remember the last remote peer after an unconnected receive.
Definition DatagramSocket.hpp:4294
std::atomic_bool _haveLocalAddr
True if _localAddr/_localAddrLen contain a valid endpoint; reset on close().
Definition DatagramSocket.hpp:4860
Port _port
Port number the socket is bound to (if applicable).
Definition DatagramSocket.hpp:4863
sockaddr_storage _remoteAddr
Storage for the address of the most recent sender (used in unconnected mode).
Definition DatagramSocket.hpp:4854
std::atomic_bool _haveRemoteAddr
Definition DatagramSocket.hpp:4857
bool _isConnected
True if the socket is connected to a remote host.
Definition DatagramSocket.hpp:4865
socklen_t _remoteAddrLen
Length of the valid address data in _remoteAddr (0 if none received yet).
Definition DatagramSocket.hpp:4855
Represents socket-related errors in the jsocketpp library.
Definition SocketException.hpp:63
Common platform and utility includes for jsocketpp.
constexpr std::size_t MaxUdpPayloadIPv6
Theoretical maximum UDP payload size (in bytes) over IPv6.
Definition common.hpp:542
std::uint16_t Port
Type alias representing a TCP or UDP port number (1–65535).
Definition common.hpp:392
constexpr std::size_t MaxDatagramPayloadSafe
Maximum UDP payload size (in bytes) that is safely valid across common stacks.
Definition common.hpp:513
constexpr std::size_t DefaultDatagramReceiveSize
Fallback receive size (in bytes) for UDP datagrams when the exact size is unknown.
Definition common.hpp:485
constexpr std::size_t MaxUdpPayloadIPv4
Maximum UDP payload size (in bytes) over IPv4.
Definition common.hpp:525
void resolveNumericHostPort(const sockaddr *sa, const socklen_t len, std::string &host, Port &port)
Resolve numeric host and port from a socket address.
Definition common.hpp:1418
void sendExact(SOCKET fd, const void *data, std::size_t size)
Sends an entire datagram to a connected peer using send().
Definition common.cpp:477
void setSocketFd(const SOCKET sock) noexcept
Updates the socket descriptor used by this object.
Definition SocketOptions.hpp:2281
SocketOptions()=delete
Default constructor (deleted) for SocketOptions base class.
SOCKET getSocketFd() const noexcept
Retrieves the native socket handle (file descriptor or OS-level handle).
Definition SocketOptions.hpp:275
void disconnect()
Disconnect this UDP socket from its current default peer.
Definition DatagramSocket.cpp:419
std::size_t readvAllWithTotalTimeoutBytes(std::span< BufferView > buffers, int totalTimeoutMillis, const DatagramReadOptions &opts={}) const
Convenience wrapper returning only the number of bytes copied.
Definition DatagramSocket.hpp:3639
std::size_t readvAllBytes(std::span< BufferView > buffers, const DatagramReadOptions &opts={}) const
Back-compat convenience returning only the number of bytes copied.
Definition DatagramSocket.hpp:3522
void cleanupAndThrow(int errorCode)
Releases all socket resources and throws a SocketException with the given error code.
Definition DatagramSocket.cpp:170
void setInternalBufferSize(std::size_t newLen)
Sets the size of the internal buffer used for string-based UDP receive operations.
Definition DatagramSocket.cpp:1786
std::size_t chooseReceiveSize() const
Decide how many bytes to attempt to receive for the next UDP datagram.
Definition DatagramSocket.hpp:4270
static std::span< const std::byte > asBytes(const std::string_view sv) noexcept
View a textual buffer as raw bytes without copying.
Definition DatagramSocket.hpp:4544
DatagramReceiveMode
Receive-time sizing policy for UDP datagrams.
Definition DatagramSocket.hpp:65
bool tryGetRemoteSockaddr(sockaddr_storage &out, socklen_t &outLen) const
Retrieve the remote endpoint sockaddr for this socket, if available.
Definition DatagramSocket.cpp:1713
void discard(const DatagramReadOptions &opts={}) const
Discard the next UDP datagram without copying it out.
Definition DatagramSocket.cpp:1295
T read(const DatagramReadOptions &opts={}) const
Read one UDP datagram into a fixed-size, contiguous byte container (zero allocation).
Definition DatagramSocket.hpp:2524
std::string getLocalSocketAddress(bool convertIPv4Mapped)
Return the local endpoint as a single "ip:port" string.
Definition DatagramSocket.cpp:1708
DatagramReadResult readvAtMostWithTimeout(std::span< BufferView > buffers, int timeoutMillis, const DatagramReadOptions &opts={}) const
Scatter-gather, best-effort read of the next datagram with a per-call timeout.
Definition DatagramSocket.cpp:1582
std::size_t readIntoBuffer(char *buf, std::size_t len, DatagramReceiveMode mode, int recvFlags, sockaddr_storage *outSrc, socklen_t *outSrcLen, std::size_t *outDatagramSz, bool *outTruncated) const
Low-level, single-recv primitive that copies one UDP datagram into a caller buffer.
Definition DatagramSocket.cpp:695
DatagramSocket & operator=(DatagramSocket &&rhs) noexcept
Move assignment operator for DatagramSocket.
Definition DatagramSocket.hpp:713
std::string getRemoteIp(bool convertIPv4Mapped) const
Return the remote peer IP address for this socket.
Definition DatagramSocket.cpp:1751
DatagramSocket(const DatagramSocket &)=delete
Copy constructor (deleted) for DatagramSocket.
void write(const T &value) const
Send one UDP datagram whose payload is the raw object representation of value.
Definition DatagramSocket.hpp:1383
static void throwSizeMismatch(const std::size_t expected, const std::size_t actual, const bool isProbedKnown)
Throw a descriptive exception when a UDP datagram’s size differs from what was expected.
Definition DatagramSocket.hpp:4343
void sendPrefixedConnected(const std::span< const std::byte > payload) const
Build and send a length-prefixed UDP datagram to the connected peer (no pre-wait).
Definition DatagramSocket.hpp:4668
void cacheLocalEndpoint() noexcept
Cache the actual local endpoint assigned by the OS.
Definition DatagramSocket.hpp:4789
void writeWithTimeout(std::string_view data, int timeoutMillis) const
Send one UDP datagram to the connected peer, waiting up to timeoutMillis for writability.
Definition DatagramSocket.cpp:509
DatagramSocket(Port localPort=0, std::string_view localAddress="", std::optional< std::size_t > recvBufferSize=std::nullopt, std::optional< std::size_t > sendBufferSize=std::nullopt, std::optional< std::size_t > internalBufferSize=std::nullopt, bool reuseAddress=true, int soRecvTimeoutMillis=-1, int soSendTimeoutMillis=-1, bool nonBlocking=false, bool dualStack=true, bool autoBind=true, bool autoConnect=false, std::string_view remoteAddress="", Port remotePort=0, int connectTimeoutMillis=-1)
Creates a UDP socket, optionally binds to a local address, and optionally connects to a remote peer.
Definition DatagramSocket.cpp:12
std::string readAtMostWithTimeout(std::size_t n, int timeoutMillis) const
Read up to n bytes from the next UDP datagram, waiting up to timeoutMillis for data.
Definition DatagramSocket.cpp:1286
void waitReady(Direction dir, int timeoutMillis) const
Block until the socket is ready for I/O or a timeout occurs.
Definition DatagramSocket.cpp:2085
void bind()
Binds the datagram socket to all available interfaces on an ephemeral port.
Definition DatagramSocket.cpp:243
void writePrefixedTo(const std::string_view host, const Port port, const std::span< const std::byte > payload) const
Send a length-prefixed UDP datagram to (host, port) from a byte span (unconnected path).
Definition DatagramSocket.hpp:1633
DatagramReadResult readvAll(std::span< BufferView > buffers, const DatagramReadOptions &opts={}) const
Scatter-gather receive that guarantees the entire next datagram fits the provided buffers.
Definition DatagramSocket.cpp:1562
void sendPrefixedUnconnected(const std::string_view host, const Port port, const std::span< const std::byte > payload)
Build and send a length-prefixed UDP datagram to (host, port) on the unconnected path.
Definition DatagramSocket.hpp:4752
void writeFrom(const void *data, std::size_t len) const
Send one UDP datagram to the connected peer from a raw memory buffer (no pre-wait).
Definition DatagramSocket.cpp:548
void cleanupAndRethrow()
Cleans up the datagram socket and rethrows the currently active exception.
Definition DatagramSocket.cpp:176
Port getRemotePort() const
Return the remote peer UDP port for this socket.
Definition DatagramSocket.cpp:1760
static std::array< std::byte, sizeof(T)> encodeLengthPrefixBE(std::size_t n)
Encode a length value into a fixed-size, big-endian (network-order) byte array.
Definition DatagramSocket.hpp:4592
void writev(std::span< const std::string_view > buffers) const
Send one UDP datagram to the connected peer by concatenating multiple fragments (no pre-wait).
Definition DatagramSocket.cpp:562
void enforceSendCapConnected(const std::size_t payloadSize) const
Enforce UDP payload size limits for connected datagram sends.
Definition DatagramSocket.hpp:4407
DatagramReadResult readFrom(T &buffer, std::string *senderAddr, Port *senderPort, const DatagramReadOptions &opts) const
Read one UDP datagram into a caller-provided byte container and optionally return the sender address/...
Definition DatagramSocket.hpp:2615
Direction
I/O readiness selector used by waitReady().
Definition DatagramSocket.hpp:413
std::string readAvailable() const
Receive the next UDP datagram and return its payload as a string, attempting to avoid truncation.
Definition DatagramSocket.cpp:1177
Port getLocalPort()
Return the local UDP port this socket is bound to.
Definition DatagramSocket.cpp:1681
void connect(std::string_view host, Port port, int timeoutMillis)
Connect this UDP socket to a default peer (set the default destination).
Definition DatagramSocket.cpp:249
DatagramReadResult readvAllWithTotalTimeout(std::span< BufferView > buffers, int totalTimeoutMillis, const DatagramReadOptions &opts={}) const
Scatter-gather, strict no-truncation receive with a per-call total timeout.
Definition DatagramSocket.cpp:1621
void write(std::string_view message) const
Send one UDP datagram to the currently connected peer (no pre-wait).
Definition DatagramSocket.cpp:458
bool hasPendingData(int timeoutMillis) const
Check if the socket is readable within a timeout (no data is consumed).
Definition DatagramSocket.cpp:1925
void close()
Closes the datagram socket and releases its underlying system resources.
Definition DatagramSocket.cpp:196
T read(const DatagramReadOptions &opts={}, std::size_t minCapacity=DefaultDatagramReceiveSize) const
Read one UDP datagram into a dynamically resizable, contiguous byte container (zero-copy into caller ...
Definition DatagramSocket.hpp:2426
void cleanup()
Internal helper that releases socket resources and resets all internal state.
Definition DatagramSocket.cpp:155
std::string getRemoteSocketAddress(bool convertIPv4Mapped) const
Return the remote endpoint as a single "ip:port" string.
Definition DatagramSocket.cpp:1769
DatagramReadResult read(DatagramPacket &packet, const DatagramReadOptions &opts) const
Read one UDP datagram into a DatagramPacket with optional growth/shrink and strict truncation policy.
Definition DatagramSocket.cpp:862
void writePrefixed(const std::string_view payload) const
Send a length-prefixed UDP datagram to the connected peer from text bytes (no pre-wait).
Definition DatagramSocket.hpp:1535
void discardExact(std::size_t n, const DatagramReadOptions &opts={}) const
Discard the next UDP datagram only if its payload size is exactly n bytes.
Definition DatagramSocket.cpp:1315
void sendUnconnectedTo(std::string_view host, Port port, const void *data, std::size_t len)
Resolve and send one unconnected UDP datagram to the first compatible destination.
Definition DatagramSocket.cpp:2168
bool isValid() const noexcept
Checks whether the datagram socket is valid and ready for use.
Definition DatagramSocket.hpp:3955
DatagramReadResult readExact(void *buffer, std::size_t exactLen, const ReadExactOptions &opts={}) const
Receive exactly exactLen bytes from a single UDP datagram into buffer, with strict policy control.
Definition DatagramSocket.cpp:1045
DatagramSocket(DatagramSocket &&rhs) noexcept
Move constructor for DatagramSocket.
Definition DatagramSocket.hpp:665
std::string getLocalIp(bool convertIPv4Mapped)
Return the local interface IP address for this UDP socket.
Definition DatagramSocket.cpp:1651
DatagramSocket & operator=(const DatagramSocket &)=delete
Copy assignment operator (deleted) for DatagramSocket.
std::string readPrefixed(std::size_t maxPayloadLen=MaxDatagramPayloadSafe, const std::endian prefixEndian=std::endian::big) const
Read a length-prefixed UDP datagram and return the payload (prefix type T).
Definition DatagramSocket.hpp:3274
void writeAll(std::string_view message) const
Send one UDP datagram to the connected peer, waiting indefinitely for writability.
Definition DatagramSocket.cpp:493
std::optional< int > getMTU() const
Retrieves the Maximum Transmission Unit (MTU) of the local interface associated with the socket.
Definition DatagramSocket.cpp:1982
std::size_t readvAtMostWithTimeout(std::span< BufferView > buffers, int timeoutMillis) const
Convenience wrapper returning only the number of bytes read.
Definition DatagramSocket.hpp:3571
std::optional< std::pair< sockaddr_storage, socklen_t > > getLastPeerSockAddr() const
Retrieves the raw socket address of the last known remote peer.
Definition DatagramSocket.hpp:4043
bool isConnected() const noexcept
Indicates whether the datagram socket is connected to a specific remote peer.
Definition DatagramSocket.hpp:1016
void writevAll(std::span< const std::string_view > buffers) const
Send one UDP datagram to the connected peer by concatenating multiple fragments, waiting indefinitely...
Definition DatagramSocket.cpp:615
void writeTo(const std::string_view host, const Port port, const T &value) const
Send one unconnected UDP datagram to (host, port) containing the raw bytes of value.
Definition DatagramSocket.hpp:2169
std::size_t readAtMost(std::span< char > out, const DatagramReadOptions &opts={}) const
Read up to out.size() bytes from the next UDP datagram into a caller-provided buffer (no allocation).
Definition DatagramSocket.cpp:1146
DatagramReadResult readInto(void *buffer, std::size_t len, const DatagramReadOptions &opts={}) const
Read one UDP datagram into a caller-provided buffer with explicit truncation policy.
Definition DatagramSocket.cpp:964
bool isBound() const noexcept
Indicates whether the datagram socket has been explicitly bound to a local address or port.
Definition DatagramSocket.hpp:880
DatagramReadResult peek(DatagramPacket &packet, bool allowResize=true, const DatagramReadOptions &opts={}) const
Peek at the next UDP datagram without consuming it (single receive with MSG_PEEK).
Definition DatagramSocket.cpp:1792
DatagramReadResult readv(std::span< BufferView > buffers, const DatagramReadOptions &opts={}) const
Scatter-gather receive: read one UDP datagram into multiple non-contiguous buffers.
Definition DatagramSocket.cpp:1372
std::size_t readIntoExact(void *buffer, std::size_t len) const
Strict exact-length UDP receive into a caller-provided buffer (single datagram).
Definition DatagramSocket.cpp:1268
bool isClosed() const noexcept
Checks whether the datagram socket has been closed or is otherwise invalid.
Definition DatagramSocket.hpp:3988
void writePrefixedTo(const std::string_view host, const Port port, const std::string_view payload) const
Send a length-prefixed UDP datagram to (host, port) from text bytes (unconnected path).
Definition DatagramSocket.hpp:1601
~DatagramSocket() noexcept override
Destructor for DatagramSocket. Ensures socket resources are released.
Definition DatagramSocket.cpp:182
DatagramReadResult readExact(T &buffer, const std::size_t exactLen, const ReadExactOptions &opts={}) const
Receive exactly exactLen bytes from a single UDP datagram into a contiguous byte container.
Definition DatagramSocket.hpp:2861
void writeTo(std::string_view host, Port port, std::string_view message)
Send one unconnected UDP datagram to (host, port) from text bytes (no pre-wait).
Definition DatagramSocket.cpp:671
void writePrefixed(const std::span< const std::byte > payload) const
Send a length-prefixed UDP datagram to the connected peer from a byte span (no pre-wait).
Definition DatagramSocket.hpp:1565
@ PreflightSize
Probe the exact size of the next datagram and size the receive accordingly.
Definition DatagramSocket.hpp:97
@ NoPreflight
Do not probe the datagram size; call recvfrom() directly.
Definition DatagramSocket.hpp:78
@ PreflightMax
Probe the size of the next datagram but cap it at the current buffer length.
Definition DatagramSocket.hpp:113
@ ReadWrite
Wait until the socket is readable or writable (logical OR).
Definition DatagramSocket.hpp:416
constexpr bool is_fixed_buffer_v
Type trait for fixed-size buffer types.
Definition buffer_traits.hpp:147
constexpr bool is_dynamic_buffer_v
Type trait for dynamic buffer types.
Definition buffer_traits.hpp:132
std::size_t nextDatagramSize(SOCKET fd) noexcept
Query the exact size of the next UDP datagram, if the platform can provide it.
Definition common.hpp:1280
A C++ socket library providing Java-style networking interfaces.
Definition BufferView.hpp:16
@ Write
Shutdown write operations (SHUT_WR or SD_SEND).
Definition common.hpp:371
@ Read
Shutdown read operations (SHUT_RD or SD_RECEIVE).
Definition common.hpp:370
std::string SocketErrorMessage(int error, bool gaiStrerror=false)
Convert a socket-related error code to a human-readable message.
Definition common.cpp:5
constexpr SOCKET INVALID_SOCKET
Definition common.hpp:264
int GetSocketError()
Definition common.hpp:246
constexpr SOCKET SOCKET_ERROR
Definition common.hpp:265
Options controlling a single UDP receive operation.
Definition DatagramSocket.hpp:136
bool allowShrink
Whether the packet buffer may shrink after receive (applies to DatagramPacket).
Definition DatagramSocket.hpp:161
bool errorOnTruncate
When true, the read fails if the incoming datagram would be truncated.
Definition DatagramSocket.hpp:220
bool resolveNumeric
Whether to resolve numeric host/port into DatagramPacket after receive.
Definition DatagramSocket.hpp:190
bool allowGrow
Whether the packet buffer may grow before receive (applies to DatagramPacket).
Definition DatagramSocket.hpp:152
DatagramReceiveMode mode
Datagram sizing policy to use during receive.
Definition DatagramSocket.hpp:143
bool updateLastRemote
Whether to persist sender into the socket's "last remote" (unconnected sockets).
Definition DatagramSocket.hpp:181
int recvFlags
Extra flags passed to recv/recvfrom (e.g., MSG_PEEK).
Definition DatagramSocket.hpp:172
Telemetry data about a single UDP datagram receive operation.
Definition DatagramSocket.hpp:249
std::size_t bytes
Number of bytes successfully copied into the destination buffer.
Definition DatagramSocket.hpp:259
socklen_t srcLen
Length in bytes of the valid address data in src.
Definition DatagramSocket.hpp:305
std::size_t datagramSize
Full size of the original datagram when it can be determined.
Definition DatagramSocket.hpp:271
sockaddr_storage src
Raw storage for the sender's address information.
Definition DatagramSocket.hpp:294
bool truncated
Indicates whether datagram truncation occurred.
Definition DatagramSocket.hpp:282
Policy for enforcing an exact-byte receive on a single UDP datagram.
Definition DatagramSocket.hpp:327
bool requireExact
Controls whether the datagram size must match exactly.
Definition DatagramSocket.hpp:351
DatagramReadOptions base
Base receive options for controlling preflight behavior, system flags, and side effects.
Definition DatagramSocket.hpp:339
bool padIfSmaller
Controls zero-padding behavior for undersized datagrams.
Definition DatagramSocket.hpp:363
bool errorOnTruncate
Controls error handling for oversized datagrams.
Definition DatagramSocket.hpp:374
bool autoResizeDynamic
Controls automatic resizing of dynamic containers.
Definition DatagramSocket.hpp:387