12 releases

0.3.3 Jan 9, 2024
0.3.2 Dec 26, 2023
0.3.0 May 11, 2023
0.2.1 Nov 11, 2020
0.0.1 Oct 14, 2020

#184 in Asynchronous

MIT license

48KB
1K SLoC

async-smux

crates.io

A lightweight asynchronous smux (Simple MUltipleXing) library for smol/async-std and any async runtime compatible to futures.

img

async-smux consumes a struct implementing AsyncRead + AsyncWrite + Unpin + Send, like TcpStream and TlsStream, to create a Mux<T> struct. And then you may spawn multiple MuxStream<T>s (up to 4294967295) over Mux<T>, which also implements AsyncRead + AsyncWrite + Unpin + Send.

Benchmark

Here is a simple benchmarking result on my local machine, comparing to the original version smux (written in go).

Implementation Throughput (TCP) Handshake
smux (go) 0.4854 GiB/s 17.070 K/s
async-smux (rust) 1.0550 GiB/s 81.774 K/s

Run cargo bench to test it by yourself. Check out /benches directory for more details.

Laziness

No thread or task will be spawned by this library. It just spawns a few futures. So it's totally runtime-independent.

Mux and MuxStream are completely lazy and will DO NOTHING if you don't poll() them.

Any polling operation, including .read() ,.write(), accept() and connect(), will push Mux and MuxStream working.

Specification

VERSION(1B) | CMD(1B) | LENGTH(2B) | STREAMID(4B) | DATA(LENGTH)

VERSION: 1

CMD:
    SYN(0)
    FIN(1)
    PSH(2)
    NOP(3)

STREAMID: Randomly chosen number

Example

use async_smux::{Mux, MuxConfig};
use async_std::net::{TcpListener, TcpStream};
use async_std::prelude::*;

async fn echo_server() {
    let listener = TcpListener::bind("0.0.0.0:12345").await.unwrap();
    let (stream, _) = listener.accept().await.unwrap();
    let mux = Mux::new(stream, MuxConfig::default());
    loop {
        let mut mux_stream = mux.accept().await.unwrap();
        let mut buf = [0u8; 1024];
        let size = mux_stream.read(&mut buf).await.unwrap();
        mux_stream.write(&buf[..size]).await.unwrap();
    }
}

fn main() {
    async_std::task::spawn(echo_server());
    async_std::task::block_on(async {
        smol::Timer::after(std::time::Duration::from_secs(1)).await;
        let stream = TcpStream::connect("127.0.0.1:12345").await.unwrap();
        let mux = Mux::new(stream, MuxConfig::default());
        for i in 0..100 {
            let mut mux_stream = mux.connect().await.unwrap();
            let mut buf = [0u8; 1024];
            mux_stream.write(b"hello").await.unwrap();
            let size = mux_stream.read(&mut buf).await.unwrap();
            let reply = String::from_utf8(buf[..size].to_vec()).unwrap();
            println!("{}: {}", i, reply);
        }
    });
}

Dependencies

~4–11MB
~104K SLoC