5 releases

0.1.4 Oct 15, 2024
0.1.3 Jul 20, 2024
0.1.2 Jul 17, 2024
0.1.1 Feb 10, 2024
0.1.0 Feb 9, 2024

#655 in Network programming

Apache-2.0

260KB
6.5K SLoC

Header

Crates.io CI Status Coverage License

Yet another project rewritten in Rust.

Features

  • Stream/Sink/Future based async API.
    • Low level API but easy to use.
  • RakNet features:
    • Support Unreliable, Reliable and ReliableOrdered packets.
    • Support multiple order channels.
    • Support ACK/NACK mechanism.
  • Full tracing:
    • You can track a packet's span during deduplication, fragmentation, ...

Roadmap

Ordered by priority

  • Documentation
  • Simulation testing
  • Bulk benchmark
  • AF_XDP socket & XDP redirect from UDP
  • Optimize performance for single thread runtime (IO without Send)
  • Robust client implementation
  • Use stable rust toolchain (I like nightly)

Getting Started

See examples or integration testing for basic usage.

Server

Most operations are performed on Stream and Sink. There will be some options in opts.

The implementation details are obscured, and you can only see a very high level of abstraction, including the Error type, which is just std::io::Error.

Keep polling incoming because it also serves as the router to every connections.

Apply Sink::poll_flush to IO will trigger to flush all pending packets, ACK/NACK, and stale packets. So you have to call poll_flush periodically. You can configure the flush strategy you want.

Apply Sink::poll_close to IO will ensure that all data is received by the peer before returning. It may keep resending infinitely unless you cancel the task. So you'd better set a timeout for each poll_close.

[!NOTE] All calculations are lazy. The state will not update if you do not poll it.

use bytes::Bytes;
use futures::{SinkExt, StreamExt};
use raknet_rs::server::{self, MakeIncoming};

let socket = tokio::net::UdpSocket::bind("127.0.0.1:0").await?;
let config = server::Config::new()
    .send_buf_cap(1024)
    .sever_guid(114514)
    .advertisement(&b"Hello, I am server"[..])
    ...
let mut incoming = socket.make_incoming(config);
let (reader, _) = incoming.next().await.unwrap();
tokio::pin!(reader);
let data: Bytes = reader.next().await.unwrap();

Client

[!WARNING] The current version of the client only has the most basic handshake implementation, and it is not recommended to use it directly.

use bytes::Bytes;
use futures::{SinkExt, StreamExt};
use raknet_rs::client::{self, ConnectTo};
use raknet_rs::Reliability;

let socket = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
let config = client::Config::new()
    .send_buf_cap(1024)
    .client_guid(1919810)
    ...
let (_, writer) = socket.connect_to(<addr>, config).await?;
tokio::pin!(writer);
writer.send(Message::new(Reliability::Reliable, 0, Bytes::from_static(b"Hello, Anyone there?")))
    .await?;

Dependencies

~3–12MB
~138K SLoC