message-io
、高速で使いやすいイベント駆動型のネットワーク ライブラリです。このライブラリは OS ソケットを内部で処理し、単純なイベント メッセージ API をユーザーに提供します。また、いくつかのルールに従って独自のトランスポート プロトコル用のアダプターを作成し、面倒な非同期やスレッド管理をライブラリに委任することもできます。
ライブラリの使用中に問題を見つけた場合、またはそれを改善するアイデアがある場合は、ためらわずに問題を開いてください。あらゆる貢献を歓迎します!そして覚えておいてください: カフェインが多ければ多いほど、生産性も高くなります。
ソケットの管理は、スレッド、同時実行性、全二重、エンコーディング、OS から発生する IO エラー (状況によっては非常に理解しにくい) などと戦う必要があるため、困難です。ノンブロッキングソケットを使用する場合は、オペレーティング システムから非同期的に送信されるイベントを同期するという、新たな複雑さの層が追加されます。
message-io
前述のすべての問題に対処する簡単な方法を提供し、独自の問題を抱えたアプリケーションを作成しようとしているプログラマにとって、それらの問題を透過的にします。そのために、ライブラリは、メッセージ(送受信するデータ) とエンドポイント(そのデータの受信者) という 2 つの概念を理解するためのシンプルな API を提供します。この抽象化により、使用されるトランスポート プロトコルに関係なく同じ API を使用できる可能性も提供されます。文字通り 1 行でアプリケーションのトランスポートを変更できます。
wasm
サポートされていませんが、予定されています)。NodeHandler
。NodeListener
。std::io::Error
を処理しません。message-io
は必要なトランスポートがありませんか?アダプターを簡単に追加できます。 Cargo.toml
に追加します (デフォルトですべてのトランスポートが含まれます)。
[ dependencies ]
message-io = " 0.18 "
利用可能なトランスポート バッテリーのサブセットのみを使用したい場合は、関連する機能tcp
、 udp
、およびwebsocket
によってそれらを選択できます。たとえば、 TCPとUDPのみを含めるには、 Cargo.toml
に次のように追加します。
[ dependencies ]
message-io = { version = " 0.18 " , default-features = false , features = [ " tcp " , " udp " ] }
次の例は、クライアントからメッセージを読み取り、同じメッセージで応答する最も単純なサーバーです。 3 つの異なるプロトコルの「サービス」を同時に提供できます。
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
} ) ;
}
次の例は、以前のサーバーに接続できるクライアントを示しています。サーバーに毎秒メッセージを送信し、そのエコー応答をリッスンします。 Transport::FramedTcp
Udp
またはWs
に変更すると、使用される基になるトランスポートが変更されます。
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 ) ) ;
}
}
} ) ;
}
リポジトリのクローンを作成し、 Ping Pongサンプルをテストします ( READMEサンプルに似ていますが、よりビタミン化されています)。
サーバーを実行します。
cargo run --example ping-pong server tcp 3456
クライアントを実行します。
cargo run --example ping-pong client tcp 127.0.0.1:3456
トランスポートを変更したり、複数のクライアントを実行したり、切断したりすることで、これを試すことができます。詳細については、ここを参照してください。
message-io
にはないトランスポート プロトコルが必要ですか?アダプターを追加してください! message-io
2種類の API を提供します。ライブラリのユーザーとしてmessage-io
自体と通信するユーザー APIと、ライブラリにプロトコル アダプターを追加したいユーザー用の内部アダプター API 。
トランスポート プロトコルをmio
の上に構築できる場合 (既存のプロトコル ライブラリのほとんどが構築可能)、それをmessage-io
に非常に簡単に追加できます。
ここで見つかった特性を実装するアダプターファイルをsrc/adapters/<my-transport-protocol>.rs
に追加します。これには、実装する必須の関数が 8 つだけ含まれており (テンプレートを参照)、アダプターの実装には約 150 行かかります。
src/network/transport.rs にあるTransport
列挙型に新しいフィールドを追加して、新しいアダプターを登録します。
それだけです。新しいトランスポートは、他のトランスポートと同様に、 message-io
API で使用できます。
おっと!もう 1 つのステップ:プル リクエストを作成して、誰もが使用できるようにします:)
message-io
を使用したオープンソース プロジェクトあなたの素晴らしいプロジェクトはmessage-io
使用していますか?プル リクエストを作成してリストに追加してください。
message-io
主な目的は、物事をシンプルにすることです。これは素晴らしいことですが、場合によっては、この視点により、すでに複雑なものがさらに複雑になる可能性があります。
たとえば、 message-io
使用すると、 async/await
パターンを使用せずに非同期ネットワーク イベントを処理できます。これにより、ネットワークからの受信メッセージを処理する複雑さが軽減され、これは非常に優れています。それにもかかわらず、非同期メッセージを読み取るアプリケーションは、これらのイベントに対しても非同期タスクを実行する傾向があります。この非同期継承はアプリケーション全体に簡単に伝播できますが、async/await パターンを使用しないと維持または拡張するのが困難です。そのような場合は、 tokio
が良い選択肢になるかもしれません。より低レベルのネットワークに対処する必要がありますが、組織とスレッド/リソースの管理が向上します。
message-io
のノード使用に関して同様の問題が発生する可能性があります。ノードはクライアント/サーバー、またはその両方として独立して使用できるため、ピアツーピア アプリケーションの作成を簡単に開始できます。実際、これはmessage-io
の意図の 1 つです。それにもかかわらず、目標が拡大すると、このパターンに関連して対処すべき問題が発生する可能性があり、 libp2p
などのライブラリには、その目標をアーカイブするのに役立つ膨大なツールが付属しています。
もちろん、これはライブラリの使用法を否定するものではありません (私も使用しています!)。ライブラリの機能について正直に説明し、探しているものに応じて適切なツールを案内するためのものです。
要約すると:
message-io
を使用して問題を単純化してください。tokio
、 libp2p
などを使用して、より詳細に制御します。