netcode/
socket.rs

1use std::io::{self};
2use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
3
4use socket2::{Domain, Protocol, Socket, Type};
5
6use crate::transceiver::Transceiver;
7
8#[derive(thiserror::Error, Debug)]
9#[error("failed to create and bind udp socket: {0}")]
10pub struct Error(#[from] std::io::Error);
11
12pub type Result<T> = std::result::Result<T, Error>;
13
14/// A wrapper around `UdpSocket` that implements the `Transceiver` trait for use in the netcode protocol.
15///
16/// `NetcodeSocket` is responsible for creating and managing a UDP socket, handling non-blocking
17/// send and receive operations, and providing the local address of the socket.
18///
19/// # Note
20///
21/// This is a lower-level component and should not be used directly unless you have a specific use case.
22/// For most applications, it is recommended to use higher-level abstractions such as `Client::new` or
23/// `Client::with_config` to create and manage clients.
24///
25/// # Example
26///
27/// ```
28/// use netcode::NetcodeSocket;
29/// use std::net::SocketAddr;
30///
31/// let addr = "127.0.0.1:41235";
32/// let send_buf_size = 256 * 1024;
33/// let recv_buf_size = 256 * 1024;
34/// let socket = NetcodeSocket::new(addr, send_buf_size, recv_buf_size).unwrap();
35/// ```
36pub struct NetcodeSocket(UdpSocket);
37
38impl NetcodeSocket {
39    pub fn new(
40        addr: impl ToSocketAddrs,
41        send_buf_size: usize,
42        recv_buf_size: usize,
43    ) -> Result<Self> {
44        let addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
45            io::Error::new(io::ErrorKind::InvalidInput, "no socket addresses found")
46        })?;
47        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
48        if addr.is_ipv6() {
49            socket.set_only_v6(true)?;
50        }
51        socket.set_send_buffer_size(send_buf_size)?;
52        socket.set_recv_buffer_size(recv_buf_size)?;
53        socket.bind(&addr.into())?;
54        socket.set_nonblocking(true)?;
55        Ok(NetcodeSocket(socket.into()))
56    }
57}
58
59impl Transceiver for NetcodeSocket {
60    type IntoError = Error;
61
62    fn addr(&self) -> SocketAddr {
63        self.0.local_addr().expect("address should be bound")
64    }
65
66    fn recv(&self, buf: &mut [u8]) -> Result<Option<(usize, SocketAddr)>> {
67        match self.0.recv_from(buf) {
68            Ok((len, addr)) if len > 0 => Ok(Some((len, addr))),
69            Ok(_) => Ok(None),
70            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
71            Err(e) => Err(Error::from(e)),
72        }
73    }
74
75    fn send(&self, buf: &[u8], addr: SocketAddr) -> Result<usize> {
76        match self.0.send_to(buf, addr) {
77            Ok(len) => Ok(len),
78            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
79            Err(e) => Err(Error::from(e)),
80        }
81    }
82}