| Crates.io | switchy_tcp |
| lib.rs | switchy_tcp |
| version | 0.1.4 |
| created_at | 2025-05-07 22:02:29.467886+00 |
| updated_at | 2025-07-21 19:28:26.68703+00 |
| description | Switchy TCP Networking package |
| homepage | |
| repository | https://github.com/MoosicBox/MoosicBox |
| max_upload_size | |
| id | 1664589 |
| size | 77,690 |
Generic TCP networking abstraction with Tokio and simulator support.
The TCP package provides:
Add this to your Cargo.toml:
[dependencies]
tcp = { path = "../tcp" }
# With specific features
tcp = {
path = "../tcp",
features = ["tokio", "simulator"]
}
#[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(())
}
#[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(())
}
#[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(())
}
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;
}
});
}
}
#[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
}
use tcp::{GenericTcpListener, Error};
use std::net::SocketAddr;
#[async_trait]
pub trait GenericTcpListener<T>: Send + Sync {
async fn accept(&self) -> Result<(T, SocketAddr), Error>;
}
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>;
}
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");
}
}
}
tokio: Enable Tokio-based TCP implementationsimulator: Enable simulator/mock TCP implementationWhen 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;