1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
use std::{
io,
net::{IpAddr, Ipv4Addr},
};
use async_std::net::{SocketAddr, UdpSocket};
use thiserror::Error;
/// Maximum size of a UDP datagram which might be sent by this crate.
///
/// This is the maximum datagram size "guaranteed" to be deliverable over any
/// reasonable network.
///
/// <https://stackoverflow.com/a/35697810/4448708>
pub const MAX_DATAGRAM_SIZE: usize = 508;
/// This struct represents a low level network socket. The socket is based on
/// UDP and thus provides unreliable and unordered means of data delivery.
pub struct Socket {
socket: UdpSocket,
port: u16,
}
impl Socket {
/// Creates / binds a new IPv4 based connection (socket).
///
/// # Arguments
///
/// * `port` - if None, system assigned port is used.
pub async fn bind(port: Option<u16>) -> io::Result<Self> {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port.unwrap_or(0));
let socket = UdpSocket::bind(addr).await?;
let obtained_port = socket.local_addr().map(|addr| addr.port())?;
if let Some(desired_port) = port {
assert_eq!(obtained_port, desired_port);
}
Ok(Self {
socket,
port: obtained_port,
})
}
pub fn port(&self) -> u16 {
self.port
}
/// Receive a single datagram.
///
/// The returned data are guaranteed to be at most [`MAX_DATAGRAM_SIZE`]
/// bytes long.
///
/// # Panics
///
/// Panics if len of `buf` is smaller than [`MAX_DATAGRAM_SIZE`].
pub async fn recv(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), RecvError> {
assert!(buf.len() >= MAX_DATAGRAM_SIZE);
self.socket
.recv_from(buf)
.await
.map(|(len, source)| (len.min(MAX_DATAGRAM_SIZE), source))
.map_err(RecvError::from)
}
/// Send data to a single target.
///
/// # Panics
///
/// This method panics if `data` have more than [`MAX_DATAGRAM_SIZE`]
/// bytes.
pub async fn send(&self, target: SocketAddr, data: &[u8]) -> Result<(), SendError> {
if data.len() > MAX_DATAGRAM_SIZE {
panic!(
"Max datagram size is {} got {}.",
MAX_DATAGRAM_SIZE,
data.len()
);
}
let n = self
.socket
.send_to(data, target)
.await
.map_err(SendError::from)?;
if n < data.len() {
Err(SendError::PartialSend(n, data.len()))
} else {
Ok(())
}
}
}
#[derive(Error, Debug)]
pub enum RecvError {
#[error("an IO error occurred")]
Io(#[from] io::Error),
}
#[derive(Error, Debug)]
pub enum SendError {
#[error("an IO error occurred")]
Io(#[from] io::Error),
#[error("only {0} of {1} bytes sent")]
PartialSend(usize, usize),
}