message-io
es una biblioteca de red basada en eventos rápida y fácil de usar. La biblioteca maneja el socket del sistema operativo internamente y ofrece una API de mensaje de evento simple al usuario. También te permite hacer un adaptador para tu propio protocolo de transporte siguiendo algunas reglas, delegando la tediosa asincronía y gestión de subprocesos a la biblioteca.
Si encuentra algún problema al utilizar la biblioteca o tiene una idea para mejorarla, no dude en abrir una edición. ¡Cualquier contribución es bienvenida! Y recuerda: ¡más cafeína, más productivo!
Administrar sockets es difícil porque hay que luchar con subprocesos, concurrencia, dúplex completo, codificación, errores de E/S que provienen del sistema operativo (que son realmente difíciles de entender en algunas situaciones), etc. Si utiliza sockets sin bloqueo , agrega una nueva capa de complejidad: sincronizar los eventos que vienen de forma asincrónica desde el Sistema Operativo.
message-io
ofrece una manera fácil de abordar todos estos problemas antes mencionados, haciéndolos transparentes para usted, el programador que quiere crear una aplicación con sus propios problemas. Para eso, la biblioteca le brinda una API simple con dos conceptos que debe comprender: mensajes (los datos que envía y recibe) y puntos finales (los destinatarios de esos datos). Esta abstracción también ofrece la posibilidad de utilizar la misma API independientemente del protocolo de transporte utilizado. Podrías cambiar el transporte de tu aplicación literalmente en una línea.
wasm
no es compatible pero está planificado).NodeHandler
para gestionar todas las conexiones (conectar, escuchar, eliminar, enviar) y señales (temporizadores, prioridad).NodeListener
para procesar todas las señales y eventos de la red.std::io::Error
interno oscuro al enviar/recibir desde la red.message-io
¿no tiene el transporte que necesitas? Agregue fácilmente un adaptador. Añade a tu Cargo.toml
(todos los transportes incluidos por defecto):
[ dependencies ]
message-io = " 0.18 "
Si solo desea utilizar un subconjunto de la batería de transporte disponible, puede seleccionarlas según sus funciones asociadas tcp
, udp
y websocket
. Por ejemplo, para incluir solo TCP y UDP , agregue a su Cargo.toml
:
[ dependencies ]
message-io = { version = " 0.18 " , default-features = false , features = [ " tcp " , " udp " ] }
El siguiente ejemplo es el servidor más simple que lee mensajes de los clientes y les responde con el mismo mensaje. Es capaz de ofrecer el "servicio" para 3 protocolos diferentes al mismo tiempo.
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
} ) ;
}
El siguiente ejemplo muestra un cliente que puede conectarse al servidor anterior. Envía un mensaje cada segundo al servidor y escucha su respuesta de eco. Cambiar Transport::FramedTcp
a Udp
o Ws
cambiará el transporte subyacente utilizado.
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 ) ) ;
}
}
} ) ;
}
Clona el repositorio y prueba el ejemplo de Ping Pong (similar al ejemplo README pero más vitaminado).
Ejecute el servidor:
cargo run --example ping-pong server tcp 3456
Ejecute el cliente:
cargo run --example ping-pong client tcp 127.0.0.1:3456
Puedes jugar con él cambiando el transporte, ejecutando varios clientes, desconectándolos, etc. Ver más aquí.
message-io
no tenga? ¡Agrega un adaptador! message-io
ofrece dos tipos de API. La API de usuario que se comunica con message-io
como usuario de la biblioteca y la API del adaptador interno para aquellos que desean agregar sus adaptadores de protocolo a la biblioteca.
Si se puede construir un protocolo de transporte encima de mio
(la mayoría de las bibliotecas de protocolos existentes pueden), entonces puedes agregarlo a message-io
muy fácilmente :
Agregue su archivo de adaptador en src/adapters/<my-transport-protocol>.rs
que implementa las características que encontrará aquí. Contiene solo 8 funciones obligatorias para implementar (consulte la plantilla) y se necesitan alrededor de 150 líneas para implementar un adaptador.
Agregue un nuevo campo en la enumeración Transport
que se encuentra en src/network/transport.rs para registrar su nuevo adaptador.
Eso es todo. Puedes usar tu nuevo transporte con la API message-io
como cualquier otro.
¡Ups! un paso más: haz un Pull Request para que todos puedan usarlo :)
message-io
¿Tu increíble proyecto utiliza message-io
? ¡Haga una solicitud de extracción y agréguela a la lista!
message-io
tiene el objetivo principal de mantener las cosas simples. Esto es genial, pero a veces este punto de vista puede hacer más complejas las cosas que ya son complejas.
Por ejemplo, message-io
permite manejar eventos de red asíncronos sin utilizar un patrón async/await
. Reduce la complejidad para manejar mensajes de ingresos de la red, lo cual es genial. Sin embargo, las aplicaciones que leen mensajes asincrónicos también tienden a realizar tareas asincrónicas sobre estos eventos. Esta herencia asincrónica se puede propagar fácilmente a toda la aplicación, siendo difícil de mantener o escalar sin un patrón asíncrono/en espera. En esos casos, quizás tokio
podría ser una mejor opción. Necesita lidiar con más cosas de red de bajo nivel, pero gana en organización y gestión de subprocesos/recursos.
Puede ocurrir un problema similar con respecto al uso del nodo de message-io
. Debido a que un nodo se puede utilizar de forma independiente como cliente/servidor o ambos, puede comenzar fácilmente a crear aplicaciones punto a punto. De hecho, esta es una de las intenciones de message-io
. Sin embargo, si su objetivo aumenta, aparecerán problemas relacionados con este patrón que resolver, y bibliotecas como libp2p
vienen con una enorme batería de herramientas para ayudar a archivar ese objetivo.
Por supuesto, esto no es un descargo de responsabilidad sobre el uso de la biblioteca (¡yo la uso!), se trata más de ser honesto acerca de sus capacidades y guiarlo hacia la herramienta adecuada dependiendo de lo que esté buscando.
Para resumir:
message-io
!tokio
, libp2p
u otros, para tener más control sobre él.