message-io
هي مكتبة شبكة سريعة وسهلة الاستخدام تعتمد على الأحداث. تتعامل المكتبة مع مقبس نظام التشغيل داخليًا وتقدم واجهة برمجة تطبيقات رسالة حدث بسيطة للمستخدم. كما يسمح لك أيضًا بإنشاء محول لبروتوكول النقل الخاص بك باتباع بعض القواعد، وتفويض إدارة التزامن والخيوط المملة إلى المكتبة.
إذا وجدت مشكلة في استخدام المكتبة أو لديك فكرة لتحسينها فلا تتردد في فتح قضية. أي مساهمة هي موضع ترحيب! وتذكر: المزيد من الكافيين، أكثر إنتاجية!
تعد إدارة المقابس أمرًا صعبًا لأنك تحتاج إلى محاربة الخيوط والتزامن والازدواج الكامل والتشفير وأخطاء الإدخال والإخراج التي تأتي من نظام التشغيل (والتي يصعب فهمها حقًا في بعض المواقف)، وما إلى ذلك. إذا كنت تستخدم مآخذ التوصيل غير المحظورة ، فهو يضيف طبقة جديدة من التعقيد: مزامنة الأحداث التي تأتي بشكل غير متزامن من نظام التشغيل.
يقدم message-io
طريقة سهلة للتعامل مع كل هذه المشاكل المذكورة أعلاه، مما يجعلها شفافة بالنسبة لك، المبرمج الذي يريد إنشاء تطبيق بمشاكله الخاصة. من أجل ذلك، تمنحك المكتبة واجهة برمجة تطبيقات بسيطة تحتوي على مفهومين يجب فهمهما: الرسائل (البيانات التي ترسلها وتستقبلها)، ونقاط النهاية (مستلمو تلك البيانات). يوفر هذا التجريد أيضًا إمكانية استخدام نفس واجهة برمجة التطبيقات (API) بشكل مستقل عن بروتوكول النقل المستخدم. يمكنك تغيير طريقة نقل طلبك في سطر واحد حرفيًا.
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 " ] }
المثال التالي هو أبسط خادم يقرأ الرسائل من العملاء ويستجيب لهم بنفس الرسالة. إنه قادر على تقديم "الخدمة" لثلاثة بروتوكولات مختلفة في نفس الوقت.
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
نوعين من واجهة برمجة التطبيقات (API). واجهة برمجة تطبيقات المستخدم التي تتحدث إلى message-io
نفسها كمستخدم للمكتبة، وواجهة برمجة تطبيقات المحول الداخلي لأولئك الذين يرغبون في إضافة محولات البروتوكول الخاصة بهم إلى المكتبة.
إذا كان من الممكن إنشاء بروتوكول نقل أعلى mio
(معظم مكتبات البروتوكولات الموجودة يمكنها ذلك)، فيمكنك إضافته إلى message-io
بسهولة :
أضف ملف المحول الخاص بك في src/adapters/<my-transport-protocol>.rs
الذي يقوم بتنفيذ السمات التي تجدها هنا. يحتوي على 8 وظائف إلزامية فقط للتنفيذ (انظر القالب)، ويستغرق تنفيذ المحول حوالي 150 سطرًا.
أضف حقلاً جديدًا في تعداد Transport
الموجود في src/network/transport.rs لتسجيل المحول الجديد.
هذا كل شيء. يمكنك استخدام وسيلة النقل الجديدة الخاصة بك مع واجهة برمجة تطبيقات message-io
مثل أي واجهة برمجة تطبيقات أخرى.
أُووبس! خطوة أخرى: قم بتقديم طلب سحب حتى يتمكن الجميع من استخدامه :)
message-io
هل يستخدم مشروعك الرائع message-io
؟ تقديم طلب سحب وإضافته إلى القائمة!
الهدف الرئيسي message-io
هو إبقاء الأمور بسيطة. وهذا أمر عظيم، ولكن في بعض الأحيان وجهة النظر هذه يمكن أن تجعل الأمور أكثر تعقيدا بالفعل.
على سبيل المثال، تسمح message-io
بمعالجة أحداث الشبكة غير المتزامنة دون استخدام نمط async/await
. فهو يقلل من تعقيد التعامل مع رسائل الدخل من الشبكة، وهو أمر عظيم. ومع ذلك، تميل التطبيقات التي تقرأ الرسائل غير المتزامنة إلى أداء مهام غير متزامنة خلال هذه الأحداث أيضًا. يمكن بسهولة نشر هذا الميراث غير المتزامن إلى التطبيق بأكمله، حيث يصعب الحفاظ عليه أو توسيع نطاقه بدون نمط غير متزامن/انتظار. في تلك الحالات، ربما يكون tokio
خيارًا أفضل. أنت بحاجة إلى التعامل مع المزيد من عناصر الشبكة ذات المستوى المنخفض ولكنك تكسب في التنظيم وإدارة الخيوط/الموارد.
يمكن أن تحدث مشكلة مماثلة فيما يتعلق باستخدام عقدة message-io
. نظرًا لأنه يمكن استخدام العقدة بشكل مستقل كعميل/خادم أو كليهما، يمكنك بسهولة البدء في إنشاء تطبيقات نظير إلى نظير. في الواقع، هذا هو أحد نوايا message-io
. ومع ذلك، إذا كان هدفك يتدرج، فستظهر مشاكل متعلقة بهذا النمط للتعامل معها، وتأتي المكتبات مثل libp2p
مع مجموعة ضخمة من الأدوات للمساعدة في أرشفة هذا الهدف.
بالطبع، هذا ليس إخلاءً للمسؤولية بشأن استخدام المكتبة (أنا أستخدمها!)، بل يتعلق الأمر بالصدق فيما يتعلق بإمكانياتها، وإرشادك إلى الأداة الصحيحة اعتمادًا على ما تبحث عنه.
لتلخيص:
message-io
!tokio
أو libp2p
أو غيرها، لتتمكن من التحكم فيها بشكل أكبر.