Struct stakker_mio::UdpQueue

source ·
pub struct UdpQueue {
    pub out: VecDeque<Vec<u8>>,
    /* private fields */
}
Expand description

Type to aid with managing a connected mio::net::UdpSocket along with MioPoll

First create the UDP socket and call connect() on it, add it to MioPoll, then pass the resulting MioSource to UdpQueue::init, which allows this struct to manage the queueing. Your ready handler should call UdpQueue::flush when the socket is WRITABLE, and UdpQueue::read repeatedly until no more packets are available when the socket is READABLE. For example:

use stakker_mio::{MioPoll, Ready, UdpQueue, mio::Interest, mio::net::UdpSocket};
use std::net::{IpAddr, SocketAddr};

fn setup_queue(cx: CX![], miopoll: MioPoll, local_ip: IpAddr, remote_addr: SocketAddr)
    -> std::io::Result<UdpQueue>
{
    // Port 0 means bind to any free emphemeral port
    let sock = UdpSocket::bind(SocketAddr::new(local_ip, 0))?;
    sock.connect(remote_addr)?;
    let sock = miopoll.add(sock, Interest::READABLE | Interest::WRITABLE, 10,
                           fwd_to!([cx], udp_ready() as (Ready)))?;
    let mut queue = UdpQueue::new();
    queue.init(sock);
    Ok(queue)
}

fn udp_ready(&mut self, cx: CX![], ready: Ready) {
    if ready.is_readable() {
        let mut tmp = [0; 4096];
        loop {
            match self.queue.read(&mut tmp) {
                Err(e) => return fail!(cx, "UDP recv error: {}", e),
                Ok(None) => break,
                Ok(Some(slice)) => {
                    // ... process packet in `slice` ...
                }
            }
        }
    }

    if ready.is_writable() {
        if let Err(e) = self.queue.flush() {
            fail!(cx, "UDP send error: {}", e);
        }
    }
}

Since the socket is assumed to be connected, no peer addresses are required when sending, and no peer addresses are returned on receiving.

Fields§

§out: VecDeque<Vec<u8>>

Output queue. To send, append packets here using out.push_back() or the UdpQueue::push call and then call UdpQueue::flush. If the OS buffer is full, then you’ll see packets building up here. However unlike TCP, UDP has no mechanism for backpressure, so any buildup should clear quickly as the OS moves the packets out of its own buffers and indicates WRITABLE again.

Implementations§

source§

impl UdpQueue

source

pub fn new() -> Self

Create a new empty UdpQueue, without any UDP socket currently associated

source

pub fn init(&mut self, socket: MioSource<UdpSocket>)

After adding a socket to the MioPoll instance with MioPoll::add, store the MioSource here to handle the buffering. UdpQueue takes care of deregistering the stream on drop. The caller should probably call UdpQueue::flush and UdpQueue::read soon after this call.

source

pub fn deinit(&mut self)

Discard the current socket if there is one, deregistering it from the MioPoll instance.

source

pub fn push(&mut self, packet: Vec<u8>)

Add a packet to the queue. You must call UdpQueue::flush soon after.

source

pub fn flush(&mut self) -> Result<()>

Flush as many packets out as possible

source

pub fn read<'a>(&mut self, tmp: &'a mut [u8]) -> Result<Option<&'a [u8]>>

Read in a packet, if available. This call is non-blocking. tmp is a scratch buffer used for fetching packets, which should be larger than the largest packet expected. If a packet is too large, it will be truncated. The largest possible UDP packet is just under 64KiB. tmp may be a reference to an array on the stack, or you might wish to keep a temporary buffer permanently allocated somewhere if you need it to be large and avoid the cost of zeroing a buffer regularly.

On reading a packet, Ok(Some(slice)) is returned, where slice is a reference into tmp. If there is no packet available, Ok(None) is returned. This method must be called repeatedly until it returns Ok(None). Only then will mio be primed to send a new READABLE ready-notification.

Packets are intentionally fetched one at a time, which allows regulating the input rate if that is required, perhaps in combination with stakker::idle!. Since there is no backpressure in UDP, if you allow too much data to build up in the OS queue then the OS may drop packets. However dropping them in the OS queue is more efficient than loading them into memory and having to drop them there.

source

pub fn read_to_vec(&mut self, tmp: &mut [u8]) -> Result<Option<Vec<u8>>>

Read in a packet, if available. Works as for UdpQueue::read, except that the data is copied into a new Vec.

Trait Implementations§

source§

impl Default for UdpQueue

source§

fn default() -> UdpQueue

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> Any for Twhere T: Any,