Skip to main content

Crate jktcp

Crate jktcp 

Source
Expand description

A userspace TCP stack that runs over any ReadWrite transport.

jktcp is intentionally simplified: it targets reliable, ordered transports (e.g. CDTunnel, a Unix socket carrying raw IP frames) where real packet loss is rare. It still implements stop-and-wait retransmission with exponential back-off so that transient glitches do not silently drop data.

§Two usage patterns

§1. AdapterStream — single-threaded, lifetime-bound

stream::AdapterStream holds a &mut reference to the adapter::Adapter, so it can only be used from the same task that owns the adapter. One stream is alive at a time; while it exists the adapter is exclusively borrowed.

Choose this when you open exactly one connection, drive it entirely from a single async task, and do not need to move the stream across tasks or store it on the heap alongside other things that reference the adapter.

use jktcp::adapter::Adapter;
use jktcp::stream::AdapterStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

let mut adapter = Adapter::new(Box::new(transport), todo!(), todo!());

let mut stream = AdapterStream::connect(&mut adapter, 1234).await?;
stream.write_all(b"hello").await?;

let mut buf = [0u8; 32];
let n = stream.read(&mut buf).await?;
println!("received: {:?}", &buf[..n]);

stream.close().await?;

§2. AdapterHandle / StreamHandle — multi-threaded, 'static

handle::AdapterHandle spawns the adapter’s I/O loop onto the Tokio runtime. All interaction happens through channels, so the resulting handle::StreamHandle is Send + Sync + 'static and can be stored in Arc, passed across tasks, or boxed as a trait object. Multiple streams can be open simultaneously.

Choose this when any of the following apply:

  • You need to open more than one connection on the same adapter.
  • The stream must cross an async task boundary (e.g. tokio::spawn).
  • You store the stream in a struct alongside other owned data (no lifetime parameter on the struct).
  • You expose the stream through an interface that requires 'static bounds (FFI, trait objects, etc.).
use jktcp::adapter::Adapter;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

let adapter = Adapter::new(Box::new(transport), todo!(), todo!());
let mut handle = adapter.to_async_handle();

let mut stream = handle.connect(1234).await?;

// stream is Send + 'static — spawn it freely
tokio::spawn(async move {
    stream.write_all(b"hello").await.unwrap();
    let mut buf = [0u8; 32];
    stream.read(&mut buf).await.unwrap();
});

§Retransmission behaviour

Both paths share the same adapter::Adapter logic:

  • Data is sent stop-and-wait: a second segment is not put on the wire until the first is acknowledged.
  • If no ACK arrives within the RTO (200 ms initially, doubling on each retry), the segment is retransmitted.
  • After 5 failed attempts the connection is closed with ErrorKind::TimedOut.
  • Out-of-order segments (sequence number ahead of expected) are dropped silently; duplicate segments (already acknowledged) are re-ACKed.

§PCAP capture

Call adapter::Adapter::pcap (or handle::AdapterHandle::pcap) with a file path before connecting to write a .pcap file that Wireshark can open.

Modules§

adapter
Core TCP state machine.
handle
Thread-safe handle over a background-task crate::adapter::Adapter.
packets
stream
Lifetime-bound stream over a borrowed Adapter.

Traits§

ReadWrite
A marker trait for types that can act as the underlying transport.