message-io
est une bibliothèque réseau basée sur les événements, rapide et facile à utiliser. La bibliothèque gère le socket du système d'exploitation en interne et propose une API de message d'événement simple à l'utilisateur. Il vous permet également de créer un adaptateur pour votre propre protocole de transport en suivant certaines règles, en déléguant l'asynchronie fastidieuse et la gestion des threads à la bibliothèque.
Si vous rencontrez un problème d'utilisation de la bibliothèque ou si vous avez une idée pour l'améliorer, n'hésitez pas à ouvrir un ticket. Toute contribution est la bienvenue ! Et rappelez-vous : plus de caféine, plus productif !
La gestion des sockets est difficile car il faut lutter contre les threads, la concurrence, le full duplex, l'encodage, les erreurs d'E/S provenant du système d'exploitation (qui sont vraiment difficiles à comprendre dans certaines situations), etc. Si vous utilisez des sockets non bloquants , cela ajoute une nouvelle couche de complexité : synchroniser les événements qui proviennent de manière asynchrone du système d'exploitation.
message-io
offre un moyen simple de résoudre tous ces problèmes mentionnés ci-dessus, en les rendant transparents pour vous, le programmeur qui souhaite créer une application avec ses propres problèmes. Pour cela, la bibliothèque vous propose une API simple avec deux concepts à comprendre : les messages (les données que vous envoyez et recevez) et les points de terminaison (les destinataires de ces données). Cette abstraction offre également la possibilité d'utiliser la même API indépendamment du protocole de transport utilisé. Vous pouvez modifier le transport de votre application en une seule ligne.
wasm
n'est pas pris en charge mais prévu).NodeHandler
pour gérer toutes les connexions (connecter, écouter, supprimer, envoyer) et les signaux (minuteries, priorité).NodeListener
pour traiter tous les signaux et événements du réseau.std::io::Error
internes sombres lors de l'envoi/réception depuis le réseau.message-io
n'a pas le transport dont vous avez besoin ? Ajoutez facilement un adaptateur. Ajoutez à votre Cargo.toml
(tous les transports inclus par défaut) :
[ dependencies ]
message-io = " 0.18 "
Si vous souhaitez uniquement utiliser un sous-ensemble de la batterie de transport disponible, vous pouvez les sélectionner en fonction de leurs fonctionnalités associées tcp
, udp
et websocket
. Par exemple, afin d'inclure uniquement TCP et UDP , ajoutez à votre Cargo.toml
:
[ dependencies ]
message-io = { version = " 0.18 " , default-features = false , features = [ " tcp " , " udp " ] }
L'exemple suivant est le serveur le plus simple qui lit les messages des clients et y répond avec le même message. Il est capable d'offrir le "service" pour 3 protocoles différents en même temps.
use message_io :: node :: { self } ;
use message_io :: network :: { NetEvent , Transport } ;
fn main ( ) {
// Create a node, the main message-io entity. It is divided in 2 parts:
// The 'handler', used to make actions (connect, send messages, signals, stop the node...)
// The 'listener', used to read events from the network or signals.
let ( handler , listener ) = node :: split :: < ( ) > ( ) ;
// Listen for TCP, UDP and WebSocket messages at the same time.
handler . network ( ) . listen ( Transport :: FramedTcp , "0.0.0.0:3042" ) . unwrap ( ) ;
handler . network ( ) . listen ( Transport :: Udp , "0.0.0.0:3043" ) . unwrap ( ) ;
handler . network ( ) . listen ( Transport :: Ws , "0.0.0.0:3044" ) . unwrap ( ) ;
// Read incoming network events.
listener . for_each ( move |event| match event . network ( ) {
NetEvent :: Connected ( _ , _ ) => unreachable ! ( ) , // Used for explicit connections.
NetEvent :: Accepted ( _endpoint , _listener ) => println ! ( "Client connected" ) , // Tcp or Ws
NetEvent :: Message ( endpoint , data ) => {
println ! ( "Received: {}" , String :: from_utf8_lossy ( data ) ) ;
handler . network ( ) . send ( endpoint , data ) ;
} ,
NetEvent :: Disconnected ( _endpoint ) => println ! ( "Client disconnected" ) , //Tcp or Ws
} ) ;
}
L'exemple suivant montre un client pouvant se connecter au serveur précédent. Il envoie un message chaque seconde au serveur et écoute sa réponse d'écho. Changer Transport::FramedTcp
en Udp
ou Ws
modifiera le transport sous-jacent utilisé.
use message_io :: node :: { self , NodeEvent } ;
use message_io :: network :: { NetEvent , Transport } ;
use std :: time :: Duration ;
enum Signal {
Greet ,
// Any other app event here.
}
fn main ( ) {
let ( handler , listener ) = node :: split ( ) ;
// You can change the transport to Udp or Ws (WebSocket).
let ( server , _ ) = handler . network ( ) . connect ( Transport :: FramedTcp , "127.0.0.1:3042" ) . unwrap ( ) ;
listener . for_each ( move |event| match event {
NodeEvent :: Network ( net_event ) => match net_event {
NetEvent :: Connected ( _endpoint , _ok ) => handler . signals ( ) . send ( Signal :: Greet ) ,
NetEvent :: Accepted ( _ , _ ) => unreachable ! ( ) , // Only generated by listening
NetEvent :: Message ( _endpoint , data ) => {
println ! ( "Received: {}" , String :: from_utf8_lossy ( data ) ) ;
} ,
NetEvent :: Disconnected ( _endpoint ) => ( ) ,
}
NodeEvent :: Signal ( signal ) => match signal {
Signal :: Greet => { // computed every second
handler . network ( ) . send ( server , "Hello server!" . as_bytes ( ) ) ;
handler . signals ( ) . send_with_timer ( Signal :: Greet , Duration :: from_secs ( 1 ) ) ;
}
}
} ) ;
}
Clonez le référentiel et testez l'exemple Ping Pong (similaire à l'exemple README mais plus vitaminé).
Exécutez le serveur :
cargo run --example ping-pong server tcp 3456
Exécutez le client :
cargo run --example ping-pong client tcp 127.0.0.1:3456
Vous pouvez jouer avec en changeant le transport, en exécutant plusieurs clients, en les déconnectant, etc. Pour en savoir plus, cliquez ici.
message-io
n'a pas ? Ajoutez un adaptateur ! message-io
propose deux types d'API. L' API utilisateur qui communique avec message-io
lui-même en tant qu'utilisateur de la bibliothèque, et l' API de l'adaptateur interne pour ceux qui souhaitent ajouter leurs adaptateurs de protocole dans la bibliothèque.
Si un protocole de transport peut être construit en haut de mio
(la plupart des bibliothèques de protocoles existantes le peuvent), alors vous pouvez l'ajouter très facilement à message-io
:
Ajoutez votre fichier d'adaptateur dans src/adapters/<my-transport-protocol>.rs
qui implémente les caractéristiques que vous trouvez ici. Il ne contient que 8 fonctions obligatoires à implémenter (voir le modèle), et il faut environ 150 lignes pour implémenter un adaptateur.
Ajoutez un nouveau champ dans l'énumération Transport
trouvée dans src/network/transport.rs pour enregistrer votre nouvel adaptateur.
C'est tout. Vous pouvez utiliser votre nouveau transport avec l'API message-io
comme n'importe quel autre.
Oups ! encore une étape : faites une Pull Request pour que tout le monde puisse l'utiliser :)
message-io
Votre projet génial utilise-t message-io
? Faites une Pull Request et ajoutez-la à la liste !
message-io
a pour objectif principal de garder les choses simples. C’est formidable, mais parfois ce point de vue pourrait rendre plus complexe des choses déjà complexes.
Par exemple, message-io
permet de gérer les événements réseau asynchrones sans utiliser de modèle async/await
. Cela réduit la complexité du traitement des messages de revenus du réseau, ce qui est formidable. Néanmoins, les applications qui lisent les messages asynchrones ont également tendance à effectuer des tâches asynchrones sur ces événements. Cet héritage asynchrone peut facilement être propagé à l'ensemble de votre application, étant difficile à maintenir ou à mettre à l'échelle sans un modèle asynchrone/attente. Dans ces cas-là, tokio
pourrait être une meilleure option. Vous devez gérer davantage de tâches réseau de bas niveau, mais vous gagnez en organisation et en gestion des threads/ressources.
Un problème similaire peut survenir concernant l'utilisation des nœuds de message-io
. Étant donné qu'un nœud peut être utilisé indépendamment en tant que client/serveur ou les deux, vous pouvez facilement commencer à créer des applications peer to peer. En fait, c'est l'une des intentions de message-io
. Néanmoins, si votre objectif évolue, des problèmes liés à ce modèle apparaîtront à résoudre, et des bibliothèques telles que libp2p
sont livrées avec une énorme batterie d'outils pour vous aider à archiver cet objectif.
Bien sûr, il ne s’agit pas d’une clause de non-responsabilité quant à l’utilisation de la bibliothèque (je l’utilise !), il s’agit plutôt d’être honnête sur ses capacités et de vous guider vers le bon outil en fonction de ce que vous recherchez.
Pour résumer :
message-io
!tokio
, libp2p
ou autres, pour avoir plus de contrôle dessus.