Skip to content

Commit e52dd7e

Browse files
committed
async UDP: Simplify to 2 rather than 3 traits
1 parent d3443f6 commit e52dd7e

File tree

2 files changed

+33
-47
lines changed

2 files changed

+33
-47
lines changed

embedded-nal-async/src/stack/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ mod tcp;
22
mod udp;
33

44
pub use tcp::TcpConnect;
5-
pub use udp::{BoundUdp, ConnectedUdp, UdpStack, UnboundUdp};
5+
pub use udp::{ConnectedUdp, UdpStack, UnconnectedUdp};

embedded-nal-async/src/stack/udp.rs

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
//! Traits for using UDP on embedded devices
2+
//!
3+
//! ## Notes for implementers
4+
//!
5+
//! * At several places, the APIs expect to provide a local address. Backends that can not obtain
6+
//! it, such as some AT-command based stacks, <!-- should question whether they may really call
7+
//! themselves UDP and --> may pretend to have performed some form of network address
8+
//! translation, and present invalid addresses as the local address.
9+
//!
10+
//! * Implementing [`UdpStack::Bound`] and [`UdpStack::Unbound`] unconnected sockets separately
11+
//! allows discarding the local addresses in the bound case. With LTO enabled, all the overhead
12+
//! compared with a third trait variant between [ConnectedUdp] and [UnconnectedUdp] (in which the
13+
//! local address is static but the remote address is flexible) should optimized out.
14+
//! Implementing `Bound` and `Unbound` with the same type is expected to be a common choice.
15+
116
use core::future::Future;
217
use no_std_net::SocketAddr;
318

@@ -48,53 +63,17 @@ pub trait ConnectedUdp {
4863

4964
/// This trait is implemented by UDP sockets.
5065
///
51-
/// The socket it represents is both bound (has a local IP address, port and interface) but not
52-
/// connected; its peer IP address is explicit in every call.
66+
/// The socket it represents is not necessarily bound (may not have a single local IP address, port
67+
/// and interface), and is typically not connected (has no remote IP address and port). Both are
68+
/// addresses are explicitly given in every call.
5369
///
54-
/// This is similar to a POSIX datagram socket that has been bound to a concrete address.
55-
pub trait BoundUdp {
70+
/// If there were constraints in place at socket creation time (typically on the local side), the
71+
/// caller MUST pass in the same (or compatible) values, MAY and pass in unspecified values where
72+
/// applicable. The implementer MAY check them for compatibility, and SHOULD do that in debug mode.
73+
pub trait UnconnectedUdp {
5674
type Error: embedded_io::Error;
5775

58-
/// Send the provided data to the connected peer
59-
fn send_to(&mut self, remote: SocketAddr, data: &[u8]) -> Self::SendToFuture<'_>;
60-
type SendToFuture<'a>: Future<Output = Result<(), Self::Error>>
61-
where
62-
Self: 'a;
63-
64-
/// Receive a datagram into the provided buffer.
65-
///
66-
/// If the received datagram exceeds the buffer's length, it is received regardless, and the
67-
/// remaining bytes are discarded. The full datagram size is still indicated in the result,
68-
/// allowing the recipient to detect that truncation.
69-
///
70-
/// The remote address is given in the result along with the number of bytes.
71-
///
72-
/// ## Compatibility note
73-
///
74-
/// This deviates from the sync/nb equivalent trait in that it describes the overflow behavior
75-
/// (a possibility not considered there). The name deviates from the original `receive()` to
76-
/// make room for a version that is more zero-copy friendly.
77-
fn receive_from_into(&mut self, buffer: &mut [u8]) -> Self::ReceiveFromIntoFuture<'_>;
78-
type ReceiveFromIntoFuture<'a>: Future<Output = Result<(usize, SocketAddr), Self::Error>>
79-
where
80-
Self: 'a;
81-
}
82-
83-
/// This trait is implemented by UDP sockets.
84-
///
85-
/// The socket it represents is neither bound (has no single local IP address, port and interface)
86-
/// nor connected (has no remote IP address and port). Both are explicitly given in every call.
87-
///
88-
/// There may be constraints placed on an unbound socket at creation time that limit the range of
89-
/// local addresses (further than the natural limitation of only using addresses assigned to the
90-
/// host).
91-
///
92-
/// A typical example of this kind of socket is a POSIX datagram socket that has been bound to
93-
/// "any" address (`[::]` or `0.0.0.0`) but to a particular port.
94-
pub trait UnboundUdp {
95-
type Error: embedded_io::Error;
96-
97-
/// Send the provided data to the connected peer
76+
/// Send the provided data to a peer
9877
///
9978
/// ## Sending initial messages
10079
///
@@ -110,6 +89,13 @@ pub trait UnboundUdp {
11089
/// sending from the address to which the original datagram was addressed, or from an unbound
11190
/// address. Both are valid choices in some situations, and the right choice depends on the
11291
/// protocol used.
92+
///
93+
/// Note that users of sockets created through [`UdpSocket::bind_single()`] should always pass
94+
/// in that single address -- even though they've made their intention clear at construction.
95+
/// They can pass either the one obtained at socket creation time, or the one obtained at
96+
/// receive time; these should be equal. This allows implementations of the trait to use a
97+
/// single kind of socket for both sockets bound to a single and sockets bound to multiple
98+
/// addresses.
11399
fn send(&mut self, local: SocketAddr, remote: SocketAddr, data: &[u8]) -> Self::SendFuture<'_>;
114100
type SendFuture<'a>: Future<Output = Result<(), Self::Error>>
115101
where
@@ -141,10 +127,10 @@ pub trait UdpStack {
141127
type Connected<'m>: ConnectedUdp
142128
where
143129
Self: 'm;
144-
type Bound<'m>: BoundUdp
130+
type Bound<'m>: UnconnectedUdp
145131
where
146132
Self: 'm;
147-
type Unbound<'m>: UnboundUdp
133+
type Unbound<'m>: UnconnectedUdp
148134
where
149135
Self: 'm;
150136

0 commit comments

Comments
 (0)