8.0 KiB
Utilisation de game-realtime-webtransport-lib
Ce guide décrit les chemins fiables natif et client navigateur/WASM exposés par game-realtime-webtransport-lib, ainsi que la capacité datagram backend-spécifique. Il ne décrit ni gameplay ni protocole wire métier.
Configuration transport
WebTransportConfig porte les limites et deadlines du chemin fiable. La configuration par défaut garde une limite de 1 MiB par message. Les deadlines sont appliquées sur les chemins natif et navigateur pour la connexion, la sélection du stream primaire et chaque envoi complet.
Exemple de configuration plus stricte :
let transport = game_realtime_webtransport_lib::WebTransportConfig::default()
.with_max_message_size(256 * 1024)
.with_connect_timeout(std::time::Duration::from_secs(5))
.with_primary_stream_timeout(std::time::Duration::from_secs(2))
.with_send_timeout(std::time::Duration::from_secs(2));
La validation effective se fait lors du bind serveur ou de la connexion client. Une limite nulle, une limite non représentable en u32 ou une deadline nulle est rejetée comme InvalidConfiguration. Côté navigateur, les deadlines doivent également tenir dans la plage u32 millisecondes imposée par le timer WASM.
Serveur natif
Créer d'abord l'identité TLS et le listener :
let identity = match game_realtime_webtransport_lib::WebTransportServerIdentity::generate_loopback() {
Ok(value) => value,
Err(error) => return Err(error),
};
let config = game_realtime_webtransport_lib::WebTransportServerConfig::new(
std::net::SocketAddr::from(([127, 0, 0, 1], 4433)),
identity,
)
.with_transport_config(transport);
let mut listener = match game_realtime_webtransport_lib::WebTransportListener::bind(config) {
Ok(value) => value,
Err(error) => return Err(error),
};
let session = match listener.accept().await {
Ok(value) => value,
Err(error) => return Err(error),
};
let connection = match session.accept_primary_connection().await {
Ok(value) => value,
Err(error) => return Err(error),
};
L'attente du prochain client dans listener.accept() n'a pas de timeout périodique. Une fois une requête WebTransport surfacée, sa réponse finale utilise la deadline de connexion configurée.
open_primary_connection() écrit l'en-tête WebTransport requis pour identifier le stream avant de retourner. Le serveur peut donc attendre accept_primary_connection() puis commencer les échanges applicatifs ; aucune frame artificielle n'est nécessaire pour rendre le stream visible.
Client natif
Le client doit connaître le SHA-256 exact du certificat serveur :
let config = match game_realtime_webtransport_lib::WebTransportClientConfig::new(
"https://127.0.0.1:4433/game",
certificate_hash,
) {
Ok(value) => value.with_transport_config(transport),
Err(error) => return Err(error),
};
let session = match game_realtime_webtransport_lib::connect(&config).await {
Ok(value) => value,
Err(error) => return Err(error),
};
let connection = match session.open_primary_connection().await {
Ok(value) => value,
Err(error) => return Err(error),
};
Le pinning est obligatoire dans cette API native ; il n'existe pas de variante qui désactive globalement la vérification TLS.
Client navigateur/WASM
Pour wasm32-unknown-unknown, les mêmes noms WebTransportCertificateHash, WebTransportClientConfig, connect et WebTransportSession::open_primary_connection() sont disponibles. Le serveur reste natif ; le navigateur est uniquement client.
Exemple de séquence Rust côté WASM :
let certificate_hash = game_realtime_webtransport_lib::WebTransportCertificateHash::from_sha256(server_sha256);
let config = match game_realtime_webtransport_lib::WebTransportClientConfig::new(
"https://127.0.0.1:4433/game",
certificate_hash,
) {
Ok(value) => value.with_transport_config(transport),
Err(error) => return Err(error),
};
let session = match game_realtime_webtransport_lib::connect(&config).await {
Ok(value) => value,
Err(error) => return Err(error),
};
let connection = match session.open_primary_connection().await {
Ok(value) => value,
Err(error) => return Err(error),
};
Le build workspace fournit web_sys_unstable_apis uniquement à wasm32-unknown-unknown. Le hash SHA-256 est transmis au navigateur comme serverCertificateHashes. La limite de message configurée est appliquée au framing WASM. connect_timeout, primary_stream_timeout et send_timeout sont réalisés par des timers WASM ; une expiration retourne TransportErrorKind::Timeout, et un send expiré reset le stream comme sur le chemin natif.
Le host Vite et l'adapter wasm-bindgen de game-realtime-webtransport-browser-smoke servent uniquement de preuve runtime. Ils ne sont pas nécessaires à un consommateur qui intègre déjà la bibliothèque dans son propre frontend.
Contrat realtime
Une fois le stream primaire sélectionné, utiliser les traits de game-realtime-transport-lib pour le chemin fiable normal :
let (mut sender, mut receiver) = game_realtime_transport_lib::RealtimeConnection::split(connection);
let message = game_realtime_transport_lib::TransportMessage::new(vec![1, 2, 3, 4]);
if let Err(error) = game_realtime_transport_lib::RealtimeSender::send(&mut sender, message).await {
return Err(error);
}
let received = match game_realtime_transport_lib::RealtimeReceiver::receive(&mut receiver).await {
Ok(value) => value,
Err(error) => return Err(error),
};
if let Err(error) = game_realtime_transport_lib::RealtimeSender::close(&mut sender).await {
return Err(error);
}
Le backend encode chaque TransportMessage sous la forme u32 big-endian + payload. Le consommateur ne doit pas reproduire ce framing lui-même.
La flow-control QUIC est respectée naturellement par l'écriture asynchrone. Sur les chemins natif et navigateur, un send qui dépasse sa deadline est considéré terminal : le stream est reset et le même sender ne doit pas être réutilisé.
Fermeture et abort
RealtimeSender::close() termine proprement la direction d'émission du stream primaire. Le pair observe ensuite TransportReceive::Closed lorsqu'il atteint le FIN après les messages déjà écrits.
Pour abandonner explicitement une direction WebTransport :
if let Err(error) = sender.abort(42) {
return Err(error);
}
ou côté réception :
if let Err(error) = receiver.abort(43) {
return Err(error);
}
Ces deux méthodes sont backend-spécifiques : elles ne sont pas ajoutées au contrat commun car WebSocket n'expose pas la même primitive QUIC de reset/stop.
Dropper un sender actif ou annuler une future send() en cours provoque un reset explicite. Dropper un receiver actif provoque un stop explicite. Cela évite qu'une cancellation d'écriture partielle soit interprétée comme une fermeture propre ou qu'une frame suivante reprenne au mauvais offset.
Une future receive() peut en revanche être annulée puis relancée : le backend conserve l'état partiel du header/payload et reprend le framing à l'octet correct.
Datagrams WebTransport
Les datagrams sont une capacité propre à WebTransportSession; ils ne passent pas par RealtimeConnection. Toujours consulter la taille courante avant émission :
let max_payload = session.max_datagram_size();
if payload.len() <= max_payload {
session.send_datagram(payload)?;
}
La réception est asynchrone :
let payload = session.receive_datagram().await?;
Ces appels ne donnent aucune garantie de livraison ni d'ordre. Une perte n'est pas une violation du transport datagram. Toute deadline de réception appartient donc au scénario consommateur ; elle n'est pas injectée dans WebTransportConfig.
Le chemin navigateur expose les mêmes noms, avec send_datagram(...).await. La surface reste backend-spécifique afin de ne pas imposer une sémantique non fiable à WebSocket ou au contrat fiable commun.