This commit is contained in:
2025-07-15 17:05:04 +02:00
commit d72254c7d0
25 changed files with 2074 additions and 0 deletions

66
src/app/app.rs Normal file
View File

@@ -0,0 +1,66 @@
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::interval;
use crate::domain::client::ClientManager;
use crate::domain::event::{Event, EventBus};
use crate::network::udp::UdpServer;
use crate::runtime::dispatcher::Dispatcher;
pub struct App {
// Communication inter-components
event_bus: EventBus,
dispatcher: Dispatcher,
event_rx: Option<mpsc::Receiver<Event>>,
// Network
udp_server: UdpServer,
// Clients
client_manager: ClientManager,
}
impl App {
pub async fn new() -> Self {
let (event_bus, event_rx) = EventBus::new();
let udp_server = UdpServer::new(event_bus.clone(), "127.0.0.1:5000").await;
let client_manager = ClientManager::new();
let dispatcher = Dispatcher::new(event_bus.clone(), udp_server.clone(), client_manager.clone()).await;
Self {
event_bus,
dispatcher,
event_rx: Some(event_rx),
udp_server,
client_manager
}
}
pub async fn start(&mut self) {
if let Some(event_rx) = self.event_rx.take() {
let dispatcher = self.dispatcher.clone();
tokio::spawn(async move {
dispatcher.start(event_rx).await;
});
}
let _ = self.udp_server.start().await;
let _ = self.tick_tasks().await;
println!("App started");
}
async fn tick_tasks(&self) {
let event_bus = self.event_bus.clone();
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(1));
loop {
// println!("Tick");
interval.tick().await;
let _ = event_bus.emit(Event::TickSeconds).await;
}
});
}
}

1
src/app/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod app;

0
src/core/mod.rs Normal file
View File

162
src/domain/client.rs Normal file
View File

@@ -0,0 +1,162 @@
//! Gestion des clients pour les connexions UDP
//!
//! Ce module fournit les structures et méthodes pour gérer les clients
//! connectés au serveur UDP, incluant leur tracking et leurs modifications.
use std::net::SocketAddr;
use std::sync::Arc;
use dashmap::DashMap;
use tokio::time::Instant;
use uuid::Uuid;
use std::hash::{Hash, Hasher};
use std::time::Duration;
/// Représente un client connecté au serveur UDP
///
/// Chaque client est identifié par un UUID unique et contient
/// son adresse réseau ainsi que l'heure de sa dernière activité.
#[derive(Debug)]
pub struct Client {
id: Uuid,
address: SocketAddr,
last_seen: Instant,
}
/// Gestionnaire threadsafe pour les clients connectés
///
/// Utilise `DashMap` pour permettre un accès concurrent sécurisé
/// aux clients depuis plusieurs threads.
#[derive(Clone)]
pub struct ClientManager {
clients: Arc<DashMap<SocketAddr, Client>>,
}
impl Client {
/// Crée un nouveau client avec un UUID généré automatiquement
pub fn new(address: SocketAddr) -> Self {
let id = Uuid::new_v4();
Self {
id,
address,
last_seen: Instant::now(),
}
}
/// Retourne le UUID unique du client
pub fn id(&self) -> Uuid {
self.id
}
/// Retourne l'adresse socket du client
pub fn address(&self) -> SocketAddr {
self.address
}
/// Retourne l'instant de la dernière activité du client
pub fn last_seen(&self) -> Instant {
self.last_seen
}
/// Met à jour l'heure de dernière activité du client à maintenant
pub fn update_last_seen(&mut self) {
self.last_seen = Instant::now();
}
}
impl Hash for Client {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl PartialEq for Client {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Client {}
impl ClientManager {
/// Crée un nouveau gestionnaire de clients vide
pub fn new() -> Self {
Self {
clients: Arc::new(DashMap::new()),
}
}
/// Ajoute un client au gestionnaire
pub fn add(&self, client: Client) {
self.clients.insert(client.address(), client);
}
/// Supprime un client du gestionnaire
pub fn remove(&self, client: Client) {
self.clients.remove(&client.address());
}
/// Vérifie si un client existe pour une adresse donnée
pub fn client_exists(&self, address: SocketAddr) -> bool {
self.clients.contains_key(&address)
}
/// Récupère une référence vers un client par son adresse
pub fn get_client_by_address(&self, address: SocketAddr) -> Option<dashmap::mapref::one::Ref<SocketAddr, Client>> {
self.clients.get(&address)
}
/// Récupère toutes les adresses des clients connectés
pub fn get_all_adresses(&self) -> Vec<SocketAddr> {
self.clients.iter().map(|entry| *entry.key()).collect()
}
/// Met à jour l'heure de dernière activité d'un client
pub fn update_client_last_seen(&self, address: SocketAddr) {
if let Some(mut client) = self.clients.get_mut(&address) {
client.update_last_seen();
}
}
/// Supprimer les clients trop vieux
pub fn cleanup(&self, max_age: Duration) {
let now = Instant::now();
self.clients.retain(|_, client| now - client.last_seen() < max_age);
}
/// Modifie un client via une closure
///
/// # Arguments
/// * `address` - L'adresse du client à modifier
/// * `f` - La closure qui recevra une référence mutable vers le client
///
/// # Returns
/// `true` si le client a été trouvé et modifié, `false` sinon
///
/// # Examples
/// ```ignore
/// let client_manager = ClientManager::new();
/// let addr = "127.0.0.1:8080".parse().unwrap();
///
/// // Mise à jour simple
/// client_manager.modify_client(addr, |client| {
/// client.update_last_seen();
/// });
///
/// // Modifications multiples
/// let success = client_manager.modify_client(addr, |client| {
/// client.update_last_seen();
/// // autres modifications...
/// });
/// ```
pub fn modify_client<F>(&self, address: SocketAddr, f: F) -> bool
where
F: FnOnce(&mut Client),
{
if let Some(mut client) = self.clients.get_mut(&address) {
f(&mut *client);
true
} else {
false
}
}
}

40
src/domain/event.rs Normal file
View File

@@ -0,0 +1,40 @@
use std::net::SocketAddr;
use tokio::sync::mpsc;
use crate::network::protocol::{UDPMessage};
#[derive(Clone, Debug)]
pub enum Event {
AppStarted,
AppStopped,
UdpStarted,
UdpStopped,
UdpIn(UDPMessage),
UdpOut(UDPMessage),
TickSeconds
}
#[derive(Clone)]
pub struct EventBus {
pub sender: mpsc::Sender<Event>,
}
impl EventBus {
pub fn new() -> (Self, mpsc::Receiver<Event>) {
let (sender, receiver) = mpsc::channel(10000);
(Self { sender }, receiver)
}
pub async fn emit(&self, event: Event) {
let _ = self.sender.send(event).await;
}
pub fn emit_sync(&self, event: Event) {
let _ = self.sender.try_send(event);
}
pub fn clone_sender(&self) -> mpsc::Sender<Event> {
self.sender.clone()
}
}

3
src/domain/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod event;
pub mod user;
pub mod client;

47
src/domain/user.rs Normal file
View File

@@ -0,0 +1,47 @@
use std::net::SocketAddr;
use std::sync::Arc;
use dashmap::DashMap;
use uuid::Uuid;
pub struct User {
id: Uuid,
udp_addr: Option<SocketAddr>,
}
#[derive(Clone)]
pub struct UserManager {
users: Arc<DashMap<Uuid, User>>,
}
impl User {
pub fn new(id: Uuid) -> Self {
Self {
id,
udp_addr: None,
}
}
pub fn default() -> Self {
Self::new(Uuid::new_v4())
}
pub fn set_udp_addr(&mut self, udp_addr: SocketAddr) {
self.udp_addr = Some(udp_addr);
}
}
impl UserManager {
pub fn new() -> Self {
Self {
users: Arc::new(DashMap::new())
}
}
pub fn add_user(&self, user: User) {
self.users.insert(user.id, user);
}
pub fn delete_user(&self, user: User) {
self.users.remove(&user.id);
}
}

6
src/lib.rs Normal file
View File

@@ -0,0 +1,6 @@
pub mod app;
pub mod core;
pub mod domain;
pub mod network;
pub mod runtime;
pub mod utils;

19
src/main.rs Normal file
View File

@@ -0,0 +1,19 @@
use tokio::signal;
use ox_speak_server_lib::app::app::App;
#[tokio::main]
async fn main() {
let mut app = App::new().await;
app.start().await;
// Attendre le signal Ctrl+C
match signal::ctrl_c().await {
Ok(()) => {
println!("Arrêt du serveur...");
}
Err(err) => {
eprintln!("Erreur lors de l'écoute du signal: {}", err);
}
}
}

2
src/network/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod protocol;
pub mod udp;

246
src/network/protocol.rs Normal file
View File

@@ -0,0 +1,246 @@
use std::collections::HashSet;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use std::net::SocketAddr;
use uuid::Uuid;
use strum::{EnumIter, FromRepr};
#[repr(u8)]
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, EnumIter, FromRepr)]
pub enum UDPMessageType {
Ping = 0,
Audio = 1,
// Futurs types ici...
}
#[derive(Debug, Clone, PartialEq)]
pub enum UDPMessageData {
// Client messages - Zero-copy avec Bytes
ClientPing { message_id: Uuid },
ClientAudio { sequence: u16, data: Bytes },
// Server messages
ServerPing { message_id: Uuid },
ServerAudio { user: Uuid, sequence: u16, data: Bytes },
}
#[derive(Debug, Clone, PartialEq)]
pub struct UDPMessage {
pub data: UDPMessageData,
pub address: SocketAddr,
pub size: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct UdpBroadcastMessage {
pub data: UDPMessageData,
pub addresses: HashSet<SocketAddr>, // ou Vec<SocketAddr> selon besoins
pub size: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ParseError {
EmptyData,
InvalidData,
InvalidMessageType,
InvalidUuid,
}
impl From<uuid::Error> for ParseError {
fn from(_: uuid::Error) -> Self {
ParseError::InvalidUuid
}
}
impl UDPMessageData {
// Parsing zero-copy depuis Bytes
pub fn from_client_bytes(mut data: Bytes) -> Result<Self, ParseError> {
if data.is_empty() {
return Err(ParseError::EmptyData);
}
let msg_type = data.get_u8(); // Consomme 1 byte
match msg_type {
0 => { // Ping
if data.remaining() < 16 {
return Err(ParseError::InvalidData);
}
let uuid_bytes = data.split_to(16); // Zero-copy split
let message_id = Uuid::from_slice(&uuid_bytes)?;
Ok(Self::ClientPing { message_id })
}
1 => { // Audio
if data.remaining() < 2 {
return Err(ParseError::InvalidData);
}
let sequence = data.get_u16(); // Big-endian par défaut
let audio_data = data; // Le reste pour l'audio
Ok(Self::ClientAudio { sequence, data: audio_data })
}
_ => Err(ParseError::InvalidMessageType),
}
}
// Constructeurs server
pub fn server_ping(message_id: Uuid) -> Self {
Self::ServerPing { message_id }
}
pub fn server_audio(user: Uuid, sequence: u16, data: Bytes) -> Self {
Self::ServerAudio { user, sequence, data }
}
// Sérialisation optimisée avec BytesMut
pub fn to_bytes(&self) -> Bytes {
match self {
Self::ServerPing { message_id } => {
let mut buf = BytesMut::with_capacity(17);
buf.put_u8(0); // Message type
buf.put_slice(message_id.as_bytes());
buf.freeze()
}
Self::ServerAudio { user, sequence, data } => {
let mut buf = BytesMut::with_capacity(19 + data.len());
buf.put_u8(1); // Message type
buf.put_slice(user.as_bytes());
buf.put_u16(*sequence);
buf.put_slice(data);
buf.freeze()
}
_ => panic!("Client messages cannot be serialized"),
}
}
pub fn to_vec(&self) -> Vec<u8> {
// pas très optimisé
self.to_bytes().to_vec()
}
pub fn message_type(&self) -> UDPMessageType {
match self {
Self::ClientPing { .. } | Self::ServerPing { .. } => UDPMessageType::Ping,
Self::ClientAudio { .. } | Self::ServerAudio { .. } => UDPMessageType::Audio,
}
}
// Calcule la taille du message sérialisé
pub fn size(&self) -> usize {
match self {
Self::ClientPing { .. } | Self::ServerPing { .. } => 17, // 1 + 16 (UUID)
Self::ClientAudio { data, .. } => 3 + data.len(), // 1 + 2 + audio_data
Self::ServerAudio { data, .. } => 19 + data.len(), // 1 + 16 + 2 + audio_data
}
}
}
impl UDPMessage {
// Parsing depuis slice -> Bytes (zero-copy si possible)
pub fn from_client_bytes(address: SocketAddr, data: &[u8]) -> Result<Self, ParseError> {
let original_size = data.len();
let bytes = Bytes::copy_from_slice(data); // Seule allocation
let data = UDPMessageData::from_client_bytes(bytes)?;
Ok(Self {
data,
address,
size: original_size
})
}
// Constructeurs server
pub fn server_ping(address: SocketAddr, message_id: Uuid) -> Self {
let data = UDPMessageData::server_ping(message_id);
let size = data.size();
Self { data, address, size }
}
pub fn server_audio(address: SocketAddr, user: Uuid, sequence: u16, data: Bytes) -> Self {
let msg_data = UDPMessageData::server_audio(user, sequence, data);
let size = msg_data.size();
Self { data: msg_data, address, size }
}
// Helpers
pub fn to_bytes(&self) -> Bytes {
self.data.to_bytes()
}
pub fn to_vec(&self) -> Vec<u8> {
self.data.to_vec()
}
pub fn message_type(&self) -> UDPMessageType {
self.data.message_type()
}
// Getters
pub fn size(&self) -> usize {
self.size
}
pub fn address(&self) -> SocketAddr {
self.address
}
}
impl UDPMessage {
// Helpers pour récupérer certain éléments des messages
pub fn get_message_id(&self) -> Option<Uuid> {
match &self.data {
UDPMessageData::ClientPing { message_id } => Some(*message_id),
UDPMessageData::ServerPing { message_id } => Some(*message_id),
_ => None,
}
}
}
// Helper pour compatibilité avec UDPMessageType
impl UDPMessageType {
pub fn from_message(data: &[u8]) -> Option<Self> {
if data.is_empty() {
return None;
}
Self::from_repr(data[0])
}
}
impl UdpBroadcastMessage {
// Constructeurs server
pub fn server_ping(addresses: HashSet<SocketAddr>, message_id: Uuid) -> Self {
let data = UDPMessageData::server_ping(message_id);
let size = data.size();
Self { data, addresses, size }
}
pub fn server_audio(addresses: HashSet<SocketAddr>, user: Uuid, sequence: u16, data: Bytes) -> Self {
let msg_data = UDPMessageData::server_audio(user, sequence, data);
let size = msg_data.size();
Self { data: msg_data, addresses, size }
}
// Conversion vers messages individuels (pour compatibilité)
pub fn to_individual_messages(&self) -> Vec<UDPMessage> {
self.addresses.iter().map(|&addr| {
UDPMessage {
data: self.data.clone(),
address: addr,
size: self.size,
}
}).collect()
}
// Helpers
pub fn to_bytes(&self) -> Bytes {
self.data.to_bytes()
}
pub fn addresses(&self) -> &HashSet<SocketAddr> {
&self.addresses
}
pub fn size(&self) -> usize {
self.size
}
}

104
src/network/udp.rs Normal file
View File

@@ -0,0 +1,104 @@
use tokio::net::UdpSocket;
use std::error::Error;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::task::AbortHandle;
use crate::domain::event::{Event, EventBus};
use crate::network::protocol::{UDPMessage, UdpBroadcastMessage};
#[derive(Clone)]
pub struct UdpServer {
event_bus: EventBus,
socket: Arc<UdpSocket>,
abort_handle: Option<AbortHandle>,
}
impl UdpServer {
pub async fn new(event_bus: EventBus, addr: &str) -> Self {
let socket = UdpSocket::bind(addr).await.unwrap();
let addr = socket.local_addr().unwrap();
println!("Socket UDP lié avec succès on {}", addr);
Self {
event_bus,
socket: Arc::new(socket),
abort_handle: None
}
}
pub async fn start(&mut self) -> Result<(), Box<dyn Error>> {
println!("Démarrage du serveur UDP...");
let event_bus = self.event_bus.clone();
let socket = self.socket.clone();
let recv_task = tokio::spawn(async move {
// Buffer réutilisable pour éviter les allocations
let mut buf = vec![0u8; 1500];
loop {
match socket.recv_from(&mut buf).await {
Ok((size, address)) => {
// Slice du buffer pour éviter de copier des données inutiles
if let Ok(message) = UDPMessage::from_client_bytes(address, &buf[..size]) {
event_bus.emit(Event::UdpIn(message)).await;
}
// Sinon, on ignore silencieusement le message malformé
}
Err(e) => {
match e.kind() {
std::io::ErrorKind::ConnectionReset |
std::io::ErrorKind::ConnectionAborted => {
// Silencieux pour les déconnexions normales
continue;
}
_ => {
println!("Erreur UDP: {}", e);
continue;
}
}
}
}
}
});
self.abort_handle = Some(recv_task.abort_handle());
Ok(())
}
pub async fn send_udp_message(&self, message: &UDPMessage) -> bool {
match self.socket.send_to(&message.to_bytes(), message.address()).await {
Ok(size) => {
self.event_bus.emit(Event::UdpOut(message.clone())).await;
true
}
Err(e) => {
println!("Erreur lors de l'envoi du message à {}: {}", message.address(), e);
false
}
}
}
pub async fn broadcast_udp_message(&self, message: &UdpBroadcastMessage) -> bool {
let bytes = message.to_bytes();
for &address in message.addresses() {
match self.socket.send_to(&bytes, address).await {
Ok(_) => {
// Emit individual event pour tracking
let individual_msg = UDPMessage {
data: message.data.clone(),
address,
size: message.size,
};
self.event_bus.emit(Event::UdpOut(individual_msg)).await;
}
Err(e) => {
println!("Erreur broadcast vers {}: {}", address, e);
}
}
}
true
}
}

193
src/network/udp_back.rs Normal file
View File

@@ -0,0 +1,193 @@
use tokio::net::UdpSocket;
use tokio::sync::RwLock;
use std::error::Error;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use dashmap::DashMap;
use tokio::task::AbortHandle;
use tokio::time::{sleep, Instant};
use crate::domain::client::Client;
use crate::domain::event::{Event, EventBus};
use crate::network::protocol::{UdpClientMessage, UdpServerMessage};
#[derive(Clone)]
pub struct UdpServer {
event_bus: EventBus,
socket: Arc<UdpSocket>,
abort_handle: Option<AbortHandle>,
clients: Arc<DashMap<SocketAddr, Client>>,
}
impl UdpServer {
pub async fn new(event_bus: EventBus, addr: &str) -> Self {
let socket = UdpSocket::bind(addr).await.unwrap();
let addr = socket.local_addr().unwrap();
println!("Socket UDP lié avec succès on {}", addr);
Self {
event_bus,
socket: Arc::new(socket),
abort_handle: None,
clients: Arc::new(DashMap::new()),
}
}
pub async fn start(&mut self) -> Result<(), Box<dyn Error>> {
println!("Démarrage du serveur UDP...");
let event_bus = self.event_bus.clone();
let socket = self.socket.clone();
let clients = self.clients.clone();
let recv_task = tokio::spawn(async move {
let mut buf = [0u8; 1500];
loop {
match socket.recv_from(&mut buf).await {
Ok((size, address)) => {
// Ajouter le client à la liste
// todo : solution vraiment pas idéal, il faudrait vraiment la repenser avec un système helo/bye
if !clients.contains_key(&address) {
let client = Client::new(address);
clients.insert(address, client);
println!("Nouveau client connecté: {}", address);
}else {
let mut client = clients.get_mut(&address).unwrap();
client.update_last_seen();
}
if let Ok(message) = UdpClientMessage::from_bytes(&buf[..size]) {
let event = Event::UdpIn { address, size, message };
event_bus.emit(event).await;
} else {
println!("Erreur lors du parsing du message de {}: {:?}", address, &buf[..size]);
}
}
Err(e) => {
match e.kind() {
std::io::ErrorKind::ConnectionReset |
std::io::ErrorKind::ConnectionAborted => {
// Silencieux pour les déconnexions normales
continue;
}
_ => {
println!("Erreur UDP: {}", e);
continue;
}
}
}
}
}
});
self.abort_handle = Some(recv_task.abort_handle());
Ok(())
}
pub async fn send(&self, address: SocketAddr, message: UdpServerMessage) -> bool {
let event_bus = self.event_bus.clone();
match self.socket.send_to(&message.to_byte(), address).await {
Ok(size) => {
event_bus.emit(Event::UdpOut { address, size, message }).await;
true
}
Err(e) => {
println!("Erreur lors de l'envoi du message à {}: {}", address, e);
// Optionnel : retirer le client si l'adresse est invalide
self.remove_client(address).await;
false
}
}
}
pub async fn group_send(&self, addr_list: Vec<SocketAddr>, message: UdpServerMessage) -> bool {
if addr_list.is_empty() {
return true;
}
let socket = self.socket.clone();
let clients = self.clients.clone();
let send_tasks: Vec<_> = addr_list.into_iter().map(|address| {
let event_bus = self.event_bus.clone();
let message_clone = message.clone();
let socket_clone = socket.clone();
let clients_clone = clients.clone();
tokio::spawn(async move {
match socket_clone.send_to(&message_clone.to_byte(), address).await {
Ok(size) => {
event_bus.emit(Event::UdpOut { address, size, message: message_clone }).await;
true
}
Err(e) => {
println!("Erreur lors de l'envoi du message à {}: {}", address, e);
// Optionnel : retirer le client si l'adresse est invalide
if clients_clone.contains_key(&address) {
clients_clone.remove(&address);
println!("Client {} retiré de la liste", address);
}
false
}
}
})
}).collect();
let mut all_success = true;
for task in send_tasks {
match task.await {
Ok(success) => {
if !success {
all_success = false;
}
}
Err(_) => {
all_success = false;
}
}
}
all_success
}
pub async fn all_send(&self, message: UdpServerMessage) -> bool {
let client_addresses = self.get_clients().await;
self.group_send(client_addresses, message).await
}
pub async fn get_clients(&self) -> Vec<SocketAddr> {
self.clients.iter()
.map(|entry| *entry.key())
.collect()
}
// Nouvelle méthode pour nettoyer les clients déconnectés
async fn remove_client(&self, address: SocketAddr) {
if self.clients.contains_key(&address){
self.clients.remove(&address);
println!("Client {} retiré de la liste", address);
}else {
println!("Client {} n'est pas dans la liste", address);
}
}
// Méthode pour nettoyer les clients inactifs
pub async fn cleanup_inactive_clients(&self) {
let timeout = Duration::from_secs(10);
let now = Instant::now();
let mut to_remove = Vec::new();
for entry in self.clients.iter() {
let address = *entry.key();
let client = entry.value();
if now.duration_since(client.last_seen()) > timeout {
to_remove.push(address);
}
}
for address in &to_remove {
println!("Suppression du client {}", address);
self.clients.remove(address);
}
}
}

87
src/runtime/dispatcher.rs Normal file
View File

@@ -0,0 +1,87 @@
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::task::AbortHandle;
use crate::domain::client::ClientManager;
use crate::domain::event::{Event, EventBus};
use crate::network::protocol::{UDPMessageType, UDPMessage};
use crate::network::udp::UdpServer;
#[derive(Clone)]
pub struct Dispatcher {
event_bus: EventBus,
udp_server: UdpServer,
client_manager: ClientManager
}
impl Dispatcher {
pub async fn new(event_bus: EventBus, udp_server: UdpServer, client_manager: ClientManager) -> Self {
Self {
event_bus,
udp_server,
client_manager,
}
}
pub async fn start(&self, mut receiver: mpsc::Receiver<Event>) {
let (udp_in_abort_handle, udp_in_sender) = self.udp_in_handler().await;
while let Some(event) = receiver.recv().await {
match event {
Event::UdpIn(message) => {
let _ = udp_in_sender.send(message).await;
// // println!("Message reçu de {}: {:?}", address, message);
// let udp_server = self.udp_server.clone();
// tokio::spawn(async move {
// match message {
// UdpClientMessage::Ping {message_id} => {
// let send = UdpServerMessage::ping(message_id);
// let _ = udp_server.all_send(send);
// }
// UdpClientMessage::Audio {sequence, data} => {
// let tmp_user_id = Uuid::new_v4();
// let send = UdpServerMessage::audio(tmp_user_id, sequence, data);
// let _ = udp_server.all_send(send).await;
// }
// }
// });
}
Event::UdpOut(message) => {
// println!("Message envoyé à {}: {:?}", address, message);
}
Event::TickSeconds => {
self.client_manager.cleanup(Duration::from_secs(10));
}
_ => {
println!("Event non prit en charge : {:?}", event)
}
}
}
}
pub async fn udp_in_handler(&self) -> (AbortHandle, mpsc::Sender<UDPMessage>) {
let (sender, mut consumer) = mpsc::channel::<UDPMessage>(1024);
let udp_server = self.udp_server.clone();
let task = tokio::spawn(async move {
while let Some(message) = consumer.recv().await {
// Traitement direct du message sans double parsing
match message.message_type() {
UDPMessageType::Ping => {
let response_message = UDPMessage::server_ping(message.address, message.get_message_id().unwrap());
let _ = udp_server.send_udp_message(&response_message);
}
UDPMessageType::Audio => {
// Traiter l'audio
}
}
}
});
(task.abort_handle(), sender)
}
}

1
src/runtime/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod dispatcher;

398
src/utils/byte_utils.rs Normal file
View File

@@ -0,0 +1,398 @@
use uuid::Uuid;
/// Helpers pour la manipulation de bytes - idéal pour les protocoles binaires
///
/// Cette structure permet de lire séquentiellement des données binaires
/// en maintenant une position de lecture interne.
pub struct ByteReader<'a> {
/// Référence vers les données à lire
data: &'a [u8],
/// Position actuelle dans le buffer de lecture
position: usize,
}
impl<'a> ByteReader<'a> {
/// Crée un nouveau lecteur de bytes à partir d'un slice
///
/// # Arguments
/// * `data` - Le slice de bytes à lire
///
/// # Example
/// ```text
/// let data = &[0x01, 0x02, 0x03, 0x04];
/// let reader = ByteReader::new(data);
/// ```
pub fn new(data: &'a [u8]) -> Self {
Self { data, position: 0 }
}
/// Retourne le nombre de bytes restants à lire
///
/// Utilise `saturating_sub` pour éviter les débordements
/// si la position dépasse la taille des données
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
/// Vérifie si tous les bytes ont été lus
///
/// # Returns
/// `true` si il n'y a plus de bytes à lire
pub fn is_empty(&self) -> bool {
self.remaining() == 0
}
/// Retourne la position actuelle de lecture
pub fn position(&self) -> usize {
self.position
}
/// Déplace la position de lecture à l'index spécifié
///
/// La position est automatiquement limitée à la taille des données
/// pour éviter les débordements
///
/// # Arguments
/// * `position` - Nouvelle position de lecture
pub fn seek(&mut self, position: usize) {
self.position = position.min(self.data.len());
}
/// Lit un byte (u8) à la position actuelle
///
/// # Returns
/// * `Ok(u8)` - La valeur lue si disponible
/// * `Err(&'static str)` - Si la fin du buffer est atteinte
pub fn read_u8(&mut self) -> Result<u8, &'static str> {
if self.position < self.data.len() {
let value = self.data[self.position];
self.position += 1;
Ok(value)
} else {
Err("Not enough data for u8")
}
}
/// Lit un entier 16-bit en big-endian
///
/// # Returns
/// * `Ok(u16)` - La valeur lue si 2 bytes sont disponibles
/// * `Err(&'static str)` - Si moins de 2 bytes sont disponibles
pub fn read_u16_be(&mut self) -> Result<u16, &'static str> {
if self.remaining() >= 2 {
let value = u16::from_be_bytes([
self.data[self.position],
self.data[self.position + 1],
]);
self.position += 2;
Ok(value)
} else {
Err("Not enough data for u16")
}
}
/// Lit un entier 32-bit en big-endian
///
/// # Returns
/// * `Ok(u32)` - La valeur lue si 4 bytes sont disponibles
/// * `Err(&'static str)` - Si moins de 4 bytes sont disponibles
pub fn read_u32_be(&mut self) -> Result<u32, &'static str> {
if self.remaining() >= 4 {
let value = u32::from_be_bytes([
self.data[self.position],
self.data[self.position + 1],
self.data[self.position + 2],
self.data[self.position + 3],
]);
self.position += 4;
Ok(value)
} else {
Err("Not enough data for u32")
}
}
/// Lit un entier 64-bit en big-endian
///
/// # Returns
/// * `Ok(u64)` - La valeur lue si 8 bytes sont disponibles
/// * `Err(&'static str)` - Si moins de 8 bytes sont disponibles
pub fn read_u64_be(&mut self) -> Result<u64, &'static str> {
if self.remaining() >= 8 {
let value = u64::from_be_bytes([
self.data[self.position],
self.data[self.position + 1],
self.data[self.position + 2],
self.data[self.position + 3],
self.data[self.position + 4],
self.data[self.position + 5],
self.data[self.position + 6],
self.data[self.position + 7],
]);
self.position += 8;
Ok(value)
} else {
Err("Not enough data for u64")
}
}
/// Lit un UUID (16 bytes) à la position actuelle
///
/// Les UUIDs sont stockés sous forme de 16 bytes consécutifs.
/// Cette méthode lit ces 16 bytes et les convertit en UUID.
///
/// # Returns
/// * `Ok(Uuid)` - L'UUID lu si 16 bytes sont disponibles
/// * `Err(&'static str)` - Si moins de 16 bytes sont disponibles
pub fn read_uuid(&mut self) -> Result<Uuid, &'static str> {
if self.remaining() >= 16 {
let uuid_bytes = self.read_fixed_bytes::<16>()?;
Ok(Uuid::from_bytes(uuid_bytes))
} else {
Err("Not enough data for UUID")
}
}
/// Lit une séquence de bytes de longueur spécifiée
///
/// # Arguments
/// * `len` - Nombre de bytes à lire
///
/// # Returns
/// * `Ok(&[u8])` - Slice des bytes lus si disponibles
/// * `Err(&'static str)` - Si pas assez de bytes disponibles
pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8], &'static str> {
if self.remaining() >= len {
let slice = &self.data[self.position..self.position + len];
self.position += len;
Ok(slice)
} else {
Err("Not enough data for bytes")
}
}
/// Lit un tableau de bytes de taille fixe définie à la compilation
///
/// Utilise les generics const pour définir la taille du tableau
///
/// # Returns
/// * `Ok([u8; N])` - Tableau de bytes lu si disponible
/// * `Err(&'static str)` - Si pas assez de bytes disponibles
pub fn read_fixed_bytes<const N: usize>(&mut self) -> Result<[u8; N], &'static str> {
if self.remaining() >= N {
let mut array = [0u8; N];
array.copy_from_slice(&self.data[self.position..self.position + N]);
self.position += N;
Ok(array)
} else {
Err("Not enough data for fixed bytes")
}
}
/// Lit tous les bytes restants dans le buffer
///
/// Après cet appel, le reader sera vide (position = taille totale)
///
/// # Returns
/// Slice contenant tous les bytes restants
pub fn read_remaining(&mut self) -> &'a [u8] {
let slice = &self.data[self.position..];
self.position = self.data.len();
slice
}
}
/// Structure pour construire séquentiellement des données binaires
///
/// Contrairement à ByteReader, cette structure possède ses propres données
/// et permet d'écrire des valeurs de différents types.
pub struct ByteWriter {
/// Buffer interne pour stocker les données écrites
data: Vec<u8>,
}
impl ByteWriter {
/// Crée un nouveau writer avec un Vec vide
pub fn new() -> Self {
Self { data: Vec::new() }
}
/// Crée un nouveau writer avec une capacité pré-allouée
///
/// Utile pour éviter les réallocations si la taille finale
/// est approximativement connue
///
/// # Arguments
/// * `capacity` - Capacité initiale du buffer
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
}
}
/// Écrit un byte (u8) dans le buffer
///
/// # Arguments
/// * `value` - Valeur à écrire
pub fn write_u8(&mut self, value: u8) {
self.data.push(value);
}
/// Écrit un entier 16-bit en big-endian
///
/// # Arguments
/// * `value` - Valeur à écrire
pub fn write_u16_be(&mut self, value: u16) {
self.data.extend_from_slice(&value.to_be_bytes());
}
/// Écrit un entier 32-bit en big-endian
///
/// # Arguments
/// * `value` - Valeur à écrire
pub fn write_u32_be(&mut self, value: u32) {
self.data.extend_from_slice(&value.to_be_bytes());
}
/// Écrit un entier 64-bit en big-endian
///
/// # Arguments
/// * `value` - Valeur à écrire
pub fn write_u64_be(&mut self, value: u64) {
self.data.extend_from_slice(&value.to_be_bytes());
}
/// Écrit une séquence de bytes dans le buffer
///
/// # Arguments
/// * `bytes` - Slice de bytes à écrire
pub fn write_bytes(&mut self, bytes: &[u8]) {
self.data.extend_from_slice(bytes);
}
/// Écrit un tableau de bytes de taille fixe
///
/// # Arguments
/// * `bytes` - Tableau de bytes à écrire
pub fn write_fixed_bytes<const N: usize>(&mut self, bytes: [u8; N]) {
self.data.extend_from_slice(&bytes);
}
/// Consomme le writer et retourne le Vec contenant les données
///
/// # Returns
/// Vec<u8> contenant toutes les données écrites
pub fn into_vec(self) -> Vec<u8> {
self.data
}
/// Retourne une référence vers les données sous forme de slice
///
/// # Returns
/// Slice des données écrites
pub fn as_slice(&self) -> &[u8] {
&self.data
}
/// Retourne la taille actuelle du buffer
pub fn len(&self) -> usize {
self.data.len()
}
/// Vérifie si le buffer est vide
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
/// Implémentation du trait Default pour ByteWriter
///
/// Permet d'utiliser ByteWriter::default() comme équivalent de ByteWriter::new()
impl Default for ByteWriter {
fn default() -> Self {
Self::new()
}
}
/// Fonctions utilitaires standalone pour la lecture directe sans état
///
/// Ces fonctions permettent de lire des valeurs à des offsets spécifiques
/// sans avoir besoin de créer un ByteReader.
/// Lit un byte à l'offset spécifié
///
/// # Arguments
/// * `data` - Slice de données source
/// * `offset` - Position de lecture
///
/// # Returns
/// * `Some(u8)` - Valeur lue si l'offset est valide
/// * `None` - Si l'offset dépasse la taille des données
pub fn read_u8_at(data: &[u8], offset: usize) -> Option<u8> {
data.get(offset).copied()
}
/// Lit un entier 16-bit big-endian à l'offset spécifié
///
/// # Arguments
/// * `data` - Slice de données source
/// * `offset` - Position de lecture
///
/// # Returns
/// * `Some(u16)` - Valeur lue si 2 bytes sont disponibles à l'offset
/// * `None` - Si pas assez de bytes disponibles
pub fn read_u16_be_at(data: &[u8], offset: usize) -> Option<u16> {
if data.len() >= offset + 2 {
Some(u16::from_be_bytes([data[offset], data[offset + 1]]))
} else {
None
}
}
/// Lit un entier 32-bit big-endian à l'offset spécifié
///
/// # Arguments
/// * `data` - Slice de données source
/// * `offset` - Position de lecture
///
/// # Returns
/// * `Some(u32)` - Valeur lue si 4 bytes sont disponibles à l'offset
/// * `None` - Si pas assez de bytes disponibles
pub fn read_u32_be_at(data: &[u8], offset: usize) -> Option<u32> {
if data.len() >= offset + 4 {
Some(u32::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]))
} else {
None
}
}
/// Lit un entier 64-bit big-endian à l'offset spécifié
///
/// # Arguments
/// * `data` - Slice de données source
/// * `offset` - Position de lecture
///
/// # Returns
/// * `Some(u64)` - Valeur lue si 8 bytes sont disponibles à l'offset
/// * `None` - Si pas assez de bytes disponibles
pub fn read_u64_be_at(data: &[u8], offset: usize) -> Option<u64> {
if data.len() >= offset + 8 {
Some(u64::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]))
} else {
None
}
}

1
src/utils/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod byte_utils;