geiserx_ts_netstack_smoltcp_socket 0.6.8

userspace netstack built on smoltcp (socket wrappers)
Documentation
# ts_netstack_smoltcp_socket (netsock)

Ergonomic sockets API layer built around [`ts_netstack_smoltcp_core`].

The idea is to take the command [`Channel`][ts_netstack_smoltcp_core::Channel] and use
it to expose an API that looks something like `std::net` or `tokio::net`, i.e. with
`UdpSocket`, `TcpStream`, `TcpListener`, etc. This is implemented here by internalizing
the `Channel` into each socket type so that they can all send commands on their own
behalf.

Socket creation is implemented by [`CreateSocket`] as a set of extension methods on
`T where T:` [`HasChannel`][ts_netstack_smoltcp_core::HasChannel].

## Example

Compare the example in [`ts_netstack_smoltcp_core`]:

```rust
use core::net::SocketAddr;
use bytes::Bytes;
use netcore::smoltcp::time::Instant;
use netcore::smoltcp::phy::Medium;
use netcore::{Response, udp, HasChannel};
use netsock::CreateSocket;

extern crate ts_netstack_smoltcp_core as netcore;
extern crate ts_netstack_smoltcp_socket as netsock;

fn main() {
    // Construct a new netstack:
    let mut stack = netcore::Netstack::new(netcore::Config::default(), Instant::ZERO);

    // Grab a channel through which we can send commands:
    let channel = stack.command_channel();

    // Process the upcoming bind and send commands in the background (request() blocks
    // for a response, hence the thread)
    let thread = std::thread::spawn(move || {
        for i in 0..2 {
            let cmd = stack.wait_for_cmd_blocking(None).unwrap();
            stack.process_one_cmd(cmd);
        }

        stack
    });

    // Send a command to bind a UDP socket:
    let sock = channel.udp_bind_blocking(([127, 0, 0, 1], 1000).into()).unwrap();
    println!("bound udp socket: {sock:?}");

    sock.send_to_blocking(([1, 2, 3, 4], 80).into(), b"hello");
    println!("sent udp packet");

    // Wait for the thread started above to finish processing the two UDP port commands:
    let mut stack = thread.join().unwrap();

    // Pump the netstack to produce the IP packet that needs to be sent out on the network:
    let (end1, end2) = netcore::Pipe::unbounded();
    stack.poll_device_io(Instant::ZERO, &mut netcore::PipeDev {
        pipe: end1,
        medium: Medium::Ip,
        mtu: 1500,
    });

    // Receive the packet from the pipe device:
    let packet = end2.rx.recv().unwrap();
    println!("packet: {packet:?}");

    // Sanity-check that the packet we got back is shaped correctly:
    assert_eq!(
        packet.len(), 
        netcore::smoltcp::wire::IPV4_HEADER_LEN + netcore::smoltcp::wire::UDP_HEADER_LEN + b"hello".len(),
    );
    assert_eq!(packet[0] >> 4, 4); // ipv4 packet
    assert!(packet.ends_with(b"hello"));
}
```