Simple non-blocking tcp connection abstraction #13

Merged
tipragot merged 2 commits from tcp-connection into main 2024-02-07 08:26:57 +00:00
Showing only changes of commit 19aade3c13 - Show all commits

View file

@ -2,9 +2,48 @@
use std::collections::LinkedList;
use std::io::{self, Read, Write};
use std::net::{TcpStream, ToSocketAddrs};
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
/// A non-blocking tcp connection.
///
/// # Example
///
/// ```rust
/// use std::io;
///
/// use bevnet::{Connection, Listener};
///
/// # fn main() -> io::Result<()> {
/// let listener = Listener::bind("127.0.0.1:23732")?;
/// let mut connection = Connection::connect("127.0.0.1:23732")?;
///
/// // The accept operation is not blocking. So we need to loop here.
/// let mut server_connection;
/// loop {
/// if let Some(new_connection) = listener.accept()? {
/// server_connection = new_connection;
/// break;
/// }
/// }
///
/// // We don't need to loop here because the send operation just appends to the send buffer.
/// connection.send(b"Hello, world!")?;
///
/// // To be sure the message has been sent, we need to update the connection.
/// while !connection.update()? {
/// // Wait until the connection is updated.
/// }
///
/// // The receive operation is not blocking. So we need to loop here.
/// loop {
/// if let Some(message) = server_connection.receive()? {
/// assert_eq!(message, b"Hello, world!");
/// break;
/// }
/// }
/// # Ok(())
/// # }
/// ```
pub struct Connection {
/// The underlying [TcpStream] used for the connection.
stream: TcpStream,
@ -49,8 +88,11 @@ impl Connection {
/// Sends a message over the connection.
///
/// Returns `true` if the message has been sent directly and `false`
/// if the message is still in the send queue.
///
/// This function is not blocking.
pub fn send(&mut self, message: &[u8]) -> io::Result<()> {
pub fn send(&mut self, message: &[u8]) -> io::Result<bool> {
// Get the length of the message as a u16.
let message_len: u16 = match message.len().try_into() {
Ok(len) => len,
@ -75,9 +117,11 @@ impl Connection {
/// Updates the connection.
///
/// This function sends any pending messages that have not been sent yet.
/// It returns `true` if there is no remaining data to send after updating
/// the connection and `false` otherwise.
///
/// This function is not blocking.
pub fn update(&mut self) -> io::Result<()> {
pub fn update(&mut self) -> io::Result<bool> {
// Looping over the send buffers.
while let Some((offset, buffer)) = self.send_buffers.front_mut() {
// Writing the buffer to the stream.
@ -95,7 +139,7 @@ impl Connection {
}
// Returning success.
Ok(())
Ok(self.send_buffers.is_empty())
}
/// Receives a byte block from the connection.
@ -177,3 +221,48 @@ impl Connection {
Ok(Some(&self.receive_buffer[..message_len as usize]))
}
}
/// A non-blocking tcp listener.
///
/// ```rust
/// use std::io;
///
/// use bevnet::{Connection, Listener};
///
/// # fn main() -> io::Result<()> {
/// let listener = Listener::bind("127.0.0.1:23732")?;
/// let mut connection = Connection::connect("127.0.0.1:23732")?;
///
/// // The accept operation is not blocking. So we need to loop here.
/// let mut server_connection;
/// loop {
/// if let Some(new_connection) = listener.accept()? {
/// server_connection = new_connection;
/// break;
/// }
/// }
/// # Ok(())
/// # }
/// ```
pub struct Listener(TcpListener);
impl Listener {
/// Creates a new listener.
pub fn bind(addr: impl ToSocketAddrs) -> io::Result<Listener> {
let listener = TcpListener::bind(addr)?;
listener.set_nonblocking(true)?;
Ok(Listener(listener))
}
/// Accepts a new [Connection].
///
/// This function is not blocking.
pub fn accept(&self) -> io::Result<Option<Connection>> {
match self.0.accept() {
Ok((stream, _)) => Connection::new(stream).map(Some),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => Ok(None),
Err(e) => Err(e),
}
}
}