moved udp test to examples, optimized buffer handling

This commit is contained in:
Rick Richardson
2016-11-20 11:40:43 -08:00
parent 71d8672aab
commit 161811de8b
4 changed files with 209 additions and 85 deletions
+10 -9
View File
@@ -34,7 +34,7 @@ pub trait CodecUdp {
///
/// The encode method also determines the destination to which the buffer should
/// be directed, which will be returned as a SocketAddr;
fn encode(&mut self, msg: Self::Out, buf: &mut Vec<u8>) -> SocketAddr;
fn encode(&mut self, msg: &Self::Out, buf: &mut Vec<u8>) -> SocketAddr;
/// Attempts to decode a frame from the provided buffer of bytes.
///
@@ -55,7 +55,7 @@ pub trait CodecUdp {
/// returned indicating why. This informs `Framed` that the stream is now
/// corrupt and should be terminated.
///
fn decode(&mut self, src: &SocketAddr, buf: &mut Vec<u8>) -> Result<Option<Self::In>, io::Error>;
fn decode(&mut self, src: &SocketAddr, buf: &[u8]) -> Result<Option<Self::In>, io::Error>;
}
/// A unified `Stream` and `Sink` interface to an underlying `Io` object, using
@@ -82,9 +82,8 @@ impl<C : CodecUdp> Stream for FramedUdp<C> {
Ok((n, addr)) => {
trace!("read {} bytes", n);
trace!("attempting to decode a frame");
if let Some(frame) = try!(self.codec.decode(&addr, &mut self.rd)) {
if let Some(frame) = try!(self.codec.decode(&addr, & self.rd[.. n])) {
trace!("frame decoded from buffer");
self.rd.clear();
return Ok(Async::Ready(Some(frame)));
}
}
@@ -109,7 +108,7 @@ impl<C : CodecUdp> Sink for FramedUdp<C> {
}
}
self.out_addr = Some(self.codec.encode(item, &mut self.wr));
self.out_addr = Some(self.codec.encode(&item, &mut self.wr));
Ok(AsyncSink::Ready)
}
@@ -118,11 +117,13 @@ impl<C : CodecUdp> Sink for FramedUdp<C> {
while !self.wr.is_empty() {
if let Some(outaddr) = self.out_addr {
trace!("writing; remaining={}", self.wr.len());
let n = try_nb!(self.socket.send_to(&self.wr, &outaddr));
let remaining = self.wr.len();
trace!("writing; remaining={}", remaining);
let n = try_nb!(self.socket.send_to(self.wr.as_slice(), &outaddr));
trace!("written {}", n);
self.wr.clear();
self.out_addr = None;
if n != self.wr.len() {
if n != remaining {
return Err(io::Error::new(io::ErrorKind::WriteZero,
"failed to write frame datagram to socket"));
}
@@ -143,7 +144,7 @@ pub fn framed_udp<C>(socket : UdpSocket, codec : C) -> FramedUdp<C> {
FramedUdp::new(
socket,
codec,
Vec::with_capacity(64 * 1024),
vec![0; 64 * 1024],
Vec::with_capacity(64 * 1024)
)
}
+85 -2
View File
@@ -2,8 +2,9 @@ use std::io;
use std::net::{self, SocketAddr, Ipv4Addr, Ipv6Addr};
use std::fmt;
use io::FramedUdp;
use futures::Async;
use futures::{Async, Future, Poll};
use mio;
use std::mem;
use reactor::{Handle, PollEvented};
@@ -49,7 +50,7 @@ impl UdpSocket {
FramedUdp::new(
self,
codec,
Vec::with_capacity(64 * 1024),
vec![0; 64 * 1024],
Vec::with_capacity(64 * 1024)
)
}
@@ -97,6 +98,33 @@ impl UdpSocket {
Err(e) => Err(e),
}
}
/// Creates a future that will write the entire contents of the buffer `buf` to
/// the stream `a` provided.
///
/// The returned future will return after data has been written to the outbound
/// socket.
/// The future will resolve to the stream as well as the buffer (for reuse if
/// needed).
///
/// Any error which happens during writing will cause both the stream and the
/// buffer to get destroyed.
///
/// The `buf` parameter here only requires the `AsRef<[u8]>` trait, which should
/// be broadly applicable to accepting data which can be converted to a slice.
/// The `Window` struct is also available in this crate to provide a different
/// window into a slice if necessary.
pub fn send_dgram<'a, T>(&'a self, buf: T, addr : &'a SocketAddr) -> SendDGram<T>
where T: AsRef<[u8]>,
{
SendDGram {
state: UdpState::Writing {
sock: self,
addr: addr,
buf: buf,
},
}
}
/// Receives data from the socket. On success, returns the number of bytes
/// read and the address from whence the data came.
@@ -261,6 +289,61 @@ impl fmt::Debug for UdpSocket {
}
}
/// A future used to write the entire contents of some data to a stream.
///
/// This is created by the [`write_all`] top-level method.
///
/// [`write_all`]: fn.write_all.html
pub struct SendDGram<'a, T> {
state: UdpState<'a, T>,
}
enum UdpState<'a, T> {
Writing {
sock: &'a UdpSocket,
buf: T,
addr: &'a SocketAddr,
},
Empty,
}
fn zero_write() -> io::Error {
io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
}
fn incomplete_write(reason : &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, reason)
}
impl<'a, T> Future for SendDGram<'a, T>
where T: AsRef<[u8]>,
{
type Item = T;
type Error = io::Error;
fn poll(&mut self) -> Poll<T, io::Error> {
match self.state {
UdpState::Writing { ref sock, ref buf, ref addr} => {
let buf = buf.as_ref();
let n = try_nb!(sock.send_to(&buf, addr));
if n == 0 {
return Err(zero_write())
}
if n != buf.len() {
return Err(incomplete_write("Failed to send entire message in datagram"))
}
}
UdpState::Empty => panic!("poll a SendAllTo after it's done"),
}
match mem::replace(&mut self.state, UdpState::Empty) {
UdpState::Writing { buf, .. } => Ok((buf).into()),
UdpState::Empty => panic!(),
}
}
}
#[cfg(unix)]
mod sys {
use std::os::unix::prelude::*;