message-io
é uma biblioteca de rede orientada a eventos rápida e fácil de usar. A biblioteca lida com o soquete do sistema operacional internamente e oferece uma API simples de mensagem de evento ao usuário. Também permite que você faça um adaptador para seu próprio protocolo de transporte seguindo algumas regras, delegando a tediosa assincronia e gerenciamento de threads para a biblioteca.
Se você encontrar algum problema ao usar a biblioteca ou tiver uma ideia para melhorá-la, não hesite em abrir um problema. Qualquer contribuição é bem-vinda! E lembre-se: mais cafeína, mais produtivo!
Gerenciar soquetes é difícil porque você precisa lutar contra threads, simultaneidade, full duplex, codificação, erros de IO que vêm do sistema operacional (que são realmente difíceis de entender em algumas situações), etc. adiciona uma nova camada de complexidade: sincronizar os eventos que vêm de forma assíncrona do sistema operacional.
message-io
oferece uma maneira fácil de lidar com todos esses problemas mencionados, tornando-os transparentes para você, programador que deseja fazer uma aplicação com seus próprios problemas. Para isso, a biblioteca oferece uma API simples com dois conceitos para entender: mensagens (os dados que você envia e recebe) e endpoints (os destinatários desses dados). Esta abstração também oferece a possibilidade de utilizar a mesma API independentemente do protocolo de transporte utilizado. Você poderia alterar o transporte do seu aplicativo literalmente em uma linha.
wasm
não é suportado, mas planejado).NodeHandler
para gerenciar todas as conexões (conectar, ouvir, remover, enviar) e sinais (temporizadores, prioridade).NodeListener
para processar todos os sinais e eventos da rede.std::io::Error
interno escuro ao enviar/receber da rede.message-io
não tem o transporte que você precisa? Adicione facilmente um adaptador. Adicione ao seu Cargo.toml
(todos os transportes incluídos por padrão):
[ dependencies ]
message-io = " 0.18 "
Se você quiser usar apenas um subconjunto da bateria de transporte disponível, poderá selecioná-los pelos recursos associados tcp
, udp
e websocket
. Por exemplo, para incluir apenas TCP e UDP , adicione ao seu Cargo.toml
:
[ dependencies ]
message-io = { version = " 0.18 " , default-features = false , features = [ " tcp " , " udp " ] }
O exemplo a seguir é o servidor mais simples que lê mensagens dos clientes e responde a eles com a mesma mensagem. É capaz de oferecer o “serviço” para 3 protocolos diferentes ao mesmo tempo.
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
} ) ;
}
O exemplo a seguir mostra um cliente que pode se conectar ao servidor anterior. Ele envia uma mensagem a cada segundo para o servidor e ouve sua resposta de eco. Alterar Transport::FramedTcp
para Udp
ou Ws
alterará o transporte subjacente usado.
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 ) ) ;
}
}
} ) ;
}
Clone o repositório e teste o exemplo Ping Pong (semelhante ao exemplo README , mas mais vitaminado).
Execute o servidor:
cargo run --example ping-pong server tcp 3456
Execute o cliente:
cargo run --example ping-pong client tcp 127.0.0.1:3456
Você pode brincar trocando o transporte, rodando vários clientes, desconectando-os, etc. Veja mais aqui.
message-io
não tenha? Adicione um adaptador! message-io
oferece dois tipos de API. A API do usuário que se comunica com o próprio message-io
como usuário da biblioteca, e a API do adaptador interno para quem deseja adicionar seus adaptadores de protocolo à biblioteca.
Se um protocolo de transporte puder ser construído em cima do mio
(a maioria das bibliotecas de protocolos existentes podem), então você poderá adicioná-lo ao message-io
com muita facilidade :
Adicione seu arquivo adaptador em src/adapters/<my-transport-protocol>.rs
que implementa as características que você encontra aqui. Ele contém apenas 8 funções obrigatórias para implementar (veja o modelo) e são necessárias cerca de 150 linhas para implementar um adaptador.
Adicione um novo campo na enumeração Transport
encontrada em src/network/transport.rs para registrar seu novo adaptador.
Isso é tudo. Você pode usar seu novo transporte com a API message-io
como qualquer outro.
Ops! mais um passo: faça um Pull Request para que todos possam usar :)
message-io
Seu projeto incrível usa message-io
? Faça uma solicitação pull e adicione-a à lista!
message-io
tem como objetivo principal manter as coisas simples. Isso é ótimo, mas às vezes esse ponto de vista pode tornar mais complexas as coisas já complexas.
Por exemplo, message-io
permite lidar com eventos de rede assíncronos sem usar um padrão async/await
. Reduz a complexidade do tratamento de mensagens de receita da rede, o que é ótimo. No entanto, as aplicações que leem mensagens assíncronas tendem a executar tarefas assíncronas também nesses eventos. Essa herança assíncrona pode ser facilmente propagada para todo o seu aplicativo, sendo difícil de manter ou escalar sem um padrão assíncrono/aguardado. Nesses casos, talvez tokio
pudesse ser uma opção melhor. Você precisa lidar com mais coisas de rede de baixo nível, mas ganha em organização e gerenciamento de threads/recursos.
Um problema semelhante pode acontecer em relação ao uso do nó message-io
. Como um nó pode ser usado independentemente como cliente/servidor ou ambos, você pode facilmente começar a criar aplicativos ponto a ponto. Na verdade, esta é uma das intenções do message-io
. No entanto, se o seu objetivo for escalado, aparecerão problemas relacionados a esse padrão para lidar, e bibliotecas como libp2p
vêm com uma enorme bateria de ferramentas para ajudar a arquivar esse objetivo.
Claro, isso não é uma isenção de responsabilidade sobre o uso da biblioteca (eu uso!), É mais uma questão de ser honesto sobre suas capacidades e de orientá-lo para a ferramenta certa, dependendo do que você está procurando.
Para resumir:
message-io
!tokio
, libp2p
ou outros, para ter mais controle sobre ele.