16#include <system_error>
34[[noreturn]]
void ThrowSocketError(
35 const std::string& operation,
36 const asio::error_code&
error)
38 if (
error == asio::error::access_denied)
40 throw PermissionDeniedError(operation +
": " +
error.message());
43 throw SocketError(operation +
": " +
error.message());
49 const std::string& host,
51 std::chrono::duration<double> timeout)
52 :
ioContext(std::make_unique<asio::io_context>()),
53 socket(std::make_unique<asio::ip::udp::socket>(*
ioContext))
55 asio::ip::udp::resolver resolver(*
ioContext);
58 const auto endpoints = resolver.resolve(
67 "Failed to resolve UDP endpoint " + host +
":" +
72 socket->open(asio::ip::udp::v4(), ec);
75 ThrowSocketError(
"Failed to create UDP socket", ec);
78 asio::connect(*
socket, endpoints, ec);
82 "Failed to connect UDP socket to " + host +
":" +
87 SetTimeout(std::chrono::duration_cast<std::chrono::milliseconds>(timeout));
91 std::uint16_t localPort,
92 std::chrono::duration<double> timeout)
93 :
ioContext(std::make_unique<asio::io_context>()),
94 socket(std::make_unique<asio::ip::udp::socket>(*
ioContext))
98 socket->open(asio::ip::udp::v4(), ec);
101 ThrowSocketError(
"Failed to create UDP socket", ec);
105 asio::ip::udp::socket::reuse_address(
true),
110 ThrowSocketError(
"Failed to configure UDP socket reuse", ec);
113 const asio::ip::udp::endpoint endpoint(
117 socket->bind(endpoint, ec);
122 "Failed to bind UDP socket to port " +
123 std::to_string(localPort),
127 SetTimeout(std::chrono::duration_cast<std::chrono::milliseconds>(timeout));
142 socket(std::move(other.socket)),
143 receiveTimeout(other.receiveTimeout)
151 if (socket !=
nullptr)
159 socket = std::move(other.socket);
160 receiveTimeout = other.receiveTimeout;
175 const std::size_t sent =
socket->send(
182 ThrowSocketError(
"Failed to send UDP datagram", ec);
185 if (sent != data.size())
187 throw SocketError(
"UDP datagram was only partially sent");
203 std::vector<std::uint8_t>
buffer(maxLen);
205 asio::error_code receiveError;
214 [&](
const asio::error_code& ec, std::size_t bytesReceived)
219 asio::error_code timerError;
220 timer.cancel(timerError);
224 [&](
const asio::error_code& ec)
233 asio::error_code cancelError;
234 socket->cancel(cancelError);
247 ThrowSocketError(
"Socket error during UDP receive", receiveError);
267 datagram.
data.resize(maxLen);
269 asio::ip::udp::endpoint sourceEndpoint;
270 asio::error_code receiveError;
277 socket->async_receive_from(
278 asio::buffer(datagram.
data),
280 [&](
const asio::error_code& ec, std::size_t bytesReceived)
283 receivedBytes = bytesReceived;
285 asio::error_code timerError;
286 timer.cancel(timerError);
290 [&](
const asio::error_code& ec)
299 asio::error_code cancelError;
300 socket->cancel(cancelError);
314 "Socket error during UDP receive",
319 datagram.
source.
address = sourceEndpoint.address().to_string();
326 const std::vector<std::uint8_t>& data,
337 asio::ip::make_address_v4(destination.
address, ec);
342 "Invalid IPv4 destination address '" +
343 destination.
address +
"': " + ec.message());
346 const asio::ip::udp::endpoint endpoint(
350 const std::size_t sent =
socket->send_to(
358 ThrowSocketError(
"Failed to send UDP datagram", ec);
361 if (sent != data.size())
363 throw SocketError(
"UDP datagram was only partially sent");
369 receiveTimeout = std::max(timeout, std::chrono::milliseconds::zero());
380 const auto endpoint =
socket->local_endpoint(ec);
385 "Failed to query local UDP endpoint",
390 endpoint.address().to_string(),
RAII wrapper around a portable IPv4 UDP socket.
std::vector< std::uint8_t > Recv(std::size_t maxLen=defaultReceiveBufferSize) const
Receive a UDP datagram from the connected peer.
void SendTo(const std::vector< std::uint8_t > &data, const UdpEndpoint &) override
Send a datagram to a specific IPv4 endpoint.
std::chrono::milliseconds receiveTimeout
Receive timeout applied to synchronous receive operations.
UdpSocket(const std::string &host, std::uint16_t port, std::chrono::duration< double > timeout=std::chrono::duration< double >(defaultTimeout))
Resolve a host and connect a UDP socket to it.
void Send(const std::vector< std::uint8_t > &data) const
Send a UDP datagram to the connected peer.
std::unique_ptr< asio::io_context > ioContext
Asio execution context owned by this socket.
UdpDatagram RecvFrom(std::size_t maxLen) override
Receive a datagram together with its source endpoint.
std::unique_ptr< asio::ip::udp::socket > socket
Underlying portable Asio UDP socket.
UdpSocket & operator=(const UdpSocket &)=delete
Not copyable.
UdpEndpoint LocalEndpoint() const
Return the local IPv4 endpoint assigned to the socket.
~UdpSocket() override
Destroy the socket and release all owned resources.
void SetTimeout(std::chrono::milliseconds timeout) override
Set the receive timeout used by subsequent receive operations.
Declares exceptions and error types used by the PROFINET IO controller stack.
std::atomic_bool timedOut
std::size_t receivedBytes
A UDP datagram together with the IPv4 address of its sender.
std::vector< std::uint8_t > data
Datagram payload.
UdpEndpoint source
IPv4 address and UDP port of the datagram sender.
Portable IPv4 UDP endpoint.
std::string address
Numeric IPv4 address in dotted-decimal notation.
std::uint16_t port
UDP port in host byte order.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
Declares the portable IPv4 UDP socket implementation.