switchy_tcp 0.1.4

Switchy TCP Networking package
Documentation

TCP

Generic TCP networking abstraction with Tokio and simulator support.

Overview

The TCP package provides:

  • Generic TCP Traits: Abstract TCP listener and stream interfaces
  • Tokio Integration: Production-ready async TCP networking
  • Simulator Support: Mock TCP networking for testing
  • Stream Splitting: Read/write half separation for concurrent operations
  • Address Handling: Local and peer address access
  • Type Safety: Generic traits for different TCP implementations

Features

Generic TCP Interface

  • GenericTcpListener: Abstract TCP listener trait
  • GenericTcpStream: Abstract TCP stream trait with read/write
  • GenericTcpStreamReadHalf: Abstract read-only stream interface
  • GenericTcpStreamWriteHalf: Abstract write-only stream interface

Implementation Support

  • Tokio TCP: Production async TCP networking
  • Simulator TCP: Mock networking for testing and development
  • Wrapper Types: Type-safe wrappers for different implementations

Stream Operations

  • AsyncRead/AsyncWrite: Tokio async I/O trait implementations
  • Stream Splitting: Separate read and write operations
  • Address Information: Access to local and peer socket addresses
  • Connection Management: Connect, accept, and close operations

Installation

Add this to your Cargo.toml:

[dependencies]
tcp = { path = "../tcp" }

# With specific features
tcp = {
    path = "../tcp",
    features = ["tokio", "simulator"]
}

Usage

Basic TCP Server (Tokio)

#[cfg(feature = "tokio")]
use tcp::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Bind to address
    let listener = TcpListener::bind("127.0.0.1:8080").await?;
    println!("Server listening on 127.0.0.1:8080");

    loop {
        // Accept connections
        let (mut stream, addr) = listener.accept().await?;
        println!("New connection from: {}", addr);

        // Spawn task to handle connection
        tokio::spawn(async move {
            if let Err(e) = handle_connection(stream).await {
                eprintln!("Connection error: {}", e);
            }
        });
    }
}

async fn handle_connection(mut stream: TcpStream) -> Result<(), Box<dyn std::error::Error>> {
    let mut buffer = [0; 1024];

    loop {
        // Read data
        let n = stream.read(&mut buffer).await?;
        if n == 0 {
            break; // Connection closed
        }

        // Echo data back
        stream.write_all(&buffer[..n]).await?;
    }

    Ok(())
}

TCP Client

#[cfg(feature = "tokio")]
use tcp::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to server
    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;

    // Get connection info
    println!("Connected to: {}", stream.peer_addr()?);
    println!("Local address: {}", stream.local_addr()?);

    // Send data
    stream.write_all(b"Hello, server!").await?;

    // Read response
    let mut buffer = [0; 1024];
    let n = stream.read(&mut buffer).await?;
    println!("Server response: {}", String::from_utf8_lossy(&buffer[..n]));

    Ok(())
}

Stream Splitting

#[cfg(feature = "tokio")]
use tcp::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

async fn handle_bidirectional_stream(stream: TcpStream) -> Result<(), Box<dyn std::error::Error>> {
    // Split into read and write halves
    let (mut read_half, mut write_half) = stream.into_split();

    // Spawn reader task
    let reader_handle = tokio::spawn(async move {
        let mut buffer = [0; 1024];
        loop {
            match read_half.read(&mut buffer).await {
                Ok(0) => break, // Connection closed
                Ok(n) => {
                    println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));
                }
                Err(e) => {
                    eprintln!("Read error: {}", e);
                    break;
                }
            }
        }
    });

    // Spawn writer task
    let writer_handle = tokio::spawn(async move {
        let messages = ["Hello", "World", "Goodbye"];
        for msg in messages {
            if let Err(e) = write_half.write_all(msg.as_bytes()).await {
                eprintln!("Write error: {}", e);
                break;
            }
            tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        }
    });

    // Wait for both tasks
    let _ = tokio::try_join!(reader_handle, writer_handle)?;
    Ok(())
}

Generic TCP Usage

use tcp::{GenericTcpListener, GenericTcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

async fn generic_server<S, R, W, L>(listener: L) -> Result<(), tcp::Error>
where
    S: GenericTcpStream<R, W>,
    R: tcp::GenericTcpStreamReadHalf,
    W: tcp::GenericTcpStreamWriteHalf,
    L: GenericTcpListener<S>,
{
    loop {
        let (mut stream, addr) = listener.accept().await?;
        println!("Connection from: {}", addr);

        tokio::spawn(async move {
            let mut buffer = [0; 1024];
            if let Ok(n) = stream.read(&mut buffer).await {
                let _ = stream.write_all(&buffer[..n]).await;
            }
        });
    }
}

Simulator Mode (Testing)

#[cfg(feature = "simulator")]
use tcp::simulator::{TcpListener, TcpStream};

#[tokio::test]
async fn test_tcp_communication() {
    // Use simulator for testing
    let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let addr = listener.local_addr().unwrap();

    // Test client connection
    let client_stream = TcpStream::connect(&addr.to_string()).await.unwrap();
    let (server_stream, _) = listener.accept().await.unwrap();

    // Test communication
    // ... test logic using mock TCP streams
}

Generic Traits

GenericTcpListener

use tcp::{GenericTcpListener, Error};
use std::net::SocketAddr;

#[async_trait]
pub trait GenericTcpListener<T>: Send + Sync {
    async fn accept(&self) -> Result<(T, SocketAddr), Error>;
}

GenericTcpStream

use tcp::{GenericTcpStream, GenericTcpStreamReadHalf, GenericTcpStreamWriteHalf};
use tokio::io::{AsyncRead, AsyncWrite};
use std::net::SocketAddr;

pub trait GenericTcpStream<R: GenericTcpStreamReadHalf, W: GenericTcpStreamWriteHalf>:
    AsyncRead + AsyncWrite + Send + Sync + Unpin
{
    fn into_split(self) -> (R, W);
    fn local_addr(&self) -> std::io::Result<SocketAddr>;
    fn peer_addr(&self) -> std::io::Result<SocketAddr>;
}

Error Handling

use tcp::{Error, TcpStream};

async fn handle_tcp_errors() {
    match TcpStream::connect("invalid-address").await {
        Ok(stream) => {
            // Handle successful connection
        }
        Err(Error::IO(io_err)) => {
            eprintln!("I/O error: {}", io_err);
        }
        Err(Error::AddrParse(parse_err)) => {
            eprintln!("Address parse error: {}", parse_err);
        }
        Err(Error::ParseInt(int_err)) => {
            eprintln!("Integer parse error: {}", int_err);
        }
        #[cfg(feature = "simulator")]
        Err(Error::Send) => {
            eprintln!("Simulator send error");
        }
    }
}

Feature Flags

  • tokio: Enable Tokio-based TCP implementation
  • simulator: Enable simulator/mock TCP implementation

Type Aliases

When features are enabled, convenient type aliases are available:

// With tokio feature
pub type TcpListener = TokioTcpListener;
pub type TcpStream = TokioTcpStream;
pub type TcpStreamReadHalf = TokioTcpStreamReadHalf;
pub type TcpStreamWriteHalf = TokioTcpStreamWriteHalf;

// With simulator feature
pub type TcpListener = SimulatorTcpListener;
pub type TcpStream = SimulatorTcpStream;
pub type TcpStreamReadHalf = SimulatorTcpStreamReadHalf;
pub type TcpStreamWriteHalf = SimulatorTcpStreamWriteHalf;

Dependencies

  • Tokio: Async runtime and I/O (optional)
  • Async-trait: Async trait support
  • Thiserror: Error handling
  • Paste: Macro utilities

Use Cases

  • Network Servers: TCP-based server applications
  • Client Applications: TCP client connections
  • Protocol Implementation: Custom network protocol development
  • Testing: Mock network communication in tests
  • Cross-platform Networking: Abstract over different TCP implementations
  • Microservices: Service-to-service TCP communication