pub struct Server { /* private fields */ }Expand description
A Tokio UDP listener backed by one deterministic server core.
Processing is sequential: one task receives a datagram, lets the core process it, and sends the optional reply from the same socket. No per-datagram tasks or locks are involved.
That sequential ownership is what makes the reply traffic class correct. The class is a socket-wide setting, and every reply explicitly applies its own — zero included — immediately before its send, so the reply about to go out is always the one that set it. Nothing else sends from this socket, not even while a send is suspended, so no reply can inherit another session’s marking. Adding a concurrent sender would break that and require per-packet control messages instead.
§Reply source address
A reply must leave from the exact address the request was sent to. An
explicit-address listener gets that from the bind: it can send from nothing
else. A wildcard listener (0.0.0.0 or [::]) cannot, because the routing
table would choose the source, so it asks the kernel for each request’s
local destination and sends that request’s reply from it.
That per-packet path exists on Linux, macOS and FreeBSD. Elsewhere a
wildcard bind is refused at construction — see
ServerRuntimeError::WildcardSourceSelectionUnsupported — rather than
served by a listener whose replies a client on a second address would
silently discard. Explicit-address listeners are unaffected everywhere.
§Example
Bind one listener, inspect the resolved endpoint, and let the caller decide when serving ends. This example type-checks without opening a socket:
use std::{net::{Ipv4Addr, SocketAddr}, time::Duration};
use irtt_server::{Server, ServerConfig};
let mut server = Server::bind(
SocketAddr::from((Ipv4Addr::LOCALHOST, 0)),
ServerConfig::default(),
)
.await?;
println!("listening on {}", server.local_addr()?);
server.run(tokio::time::sleep(Duration::from_secs(60))).await?;Implementations§
Source§impl Server
impl Server
Sourcepub async fn bind(
addr: SocketAddr,
config: ServerConfig,
) -> Result<Self, ServerRuntimeError>
pub async fn bind( addr: SocketAddr, config: ServerConfig, ) -> Result<Self, ServerRuntimeError>
Binds one UDP listener at addr and creates its independent session
namespace.
A wildcard addr additionally configures reply source selection, and
fails where that is unavailable; see Server::from_socket.
Examples found in repository?
19async fn main() {
20 let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
21 let config = ServerConfig::default();
22
23 let mut server = match Server::bind(bind_addr, config).await {
24 Ok(server) => server,
25 Err(err) => {
26 eprintln!("failed to bind server: {err}");
27 return;
28 }
29 };
30
31 println!(
32 "listening on {}",
33 server
34 .local_addr()
35 .expect("bound socket has a local address")
36 );
37
38 let result = server.run(tokio::time::sleep(Duration::from_secs(2))).await;
39 if let Err(err) = result {
40 eprintln!("server stopped with an error: {err}");
41 }
42}Sourcepub fn from_socket(
socket: UdpSocket,
config: ServerConfig,
) -> Result<Self, ServerRuntimeError>
pub fn from_socket( socket: UdpSocket, config: ServerConfig, ) -> Result<Self, ServerRuntimeError>
Wraps an already prepared Tokio UDP socket in a server.
§Errors
Construction is fallible because a wildcard listener has setup to do before it may serve anything. It fails when the socket’s bound address cannot be queried — which is what says whether this is a wildcard listener at all — and, for a wildcard socket, when this target has no reply source selection or the kernel refuses to configure it.
Failing here is the point. A wildcard listener that cannot recover a request’s local destination would start, run, and answer clients on a second local address from an endpoint they never contacted, which they discard as though the network had dropped it.
Sourcepub fn local_addr(&self) -> Result<SocketAddr, ServerRuntimeError>
pub fn local_addr(&self) -> Result<SocketAddr, ServerRuntimeError>
Returns the local endpoint selected for the listener.
Examples found in repository?
19async fn main() {
20 let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
21 let config = ServerConfig::default();
22
23 let mut server = match Server::bind(bind_addr, config).await {
24 Ok(server) => server,
25 Err(err) => {
26 eprintln!("failed to bind server: {err}");
27 return;
28 }
29 };
30
31 println!(
32 "listening on {}",
33 server
34 .local_addr()
35 .expect("bound socket has a local address")
36 );
37
38 let result = server.run(tokio::time::sleep(Duration::from_secs(2))).await;
39 if let Err(err) = result {
40 eprintln!("server stopped with an error: {err}");
41 }
42}Sourcepub async fn run<F>(&mut self, shutdown: F) -> Result<(), ServerRuntimeError>
pub async fn run<F>(&mut self, shutdown: F) -> Result<(), ServerRuntimeError>
Serves datagrams until shutdown completes.
Graceful shutdown sends no session-close packets and leaves no hidden receive task behind, even while a reply send is waiting for socket writability. Per-packet send failures and short sends drop only that reply, and so does a failure to apply a marking that reply needed — preparing one packet is not a reason to stop serving every other session, and the next reply makes its own independent attempt. Receive failures and internal core failures terminate the loop with an error.
A reply whose marking could not be applied is not sent instead: sending
it would put it on the wire under the previous reply’s marking. An
unmarked reply on a socket this server has never marked is the one
exception, so a host that does not support the option at all — some
Windows builds do not support IP_TOS — still runs a working server.
A wildcard listener drops a request whose local destination did not arrive with it, before the core sees it. There is no fall back to a routing-table source: the listener promised a correct reply source when it accepted the bind, and a request it cannot answer correctly must not move a session’s receive, rate or lifetime state either.
Examples found in repository?
19async fn main() {
20 let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
21 let config = ServerConfig::default();
22
23 let mut server = match Server::bind(bind_addr, config).await {
24 Ok(server) => server,
25 Err(err) => {
26 eprintln!("failed to bind server: {err}");
27 return;
28 }
29 };
30
31 println!(
32 "listening on {}",
33 server
34 .local_addr()
35 .expect("bound socket has a local address")
36 );
37
38 let result = server.run(tokio::time::sleep(Duration::from_secs(2))).await;
39 if let Err(err) = result {
40 eprintln!("server stopped with an error: {err}");
41 }
42}