message-io
เป็นไลบรารีเครือข่ายที่ขับเคลื่อนด้วยเหตุการณ์ที่รวดเร็วและใช้งานง่าย ไลบรารีจะจัดการซ็อกเก็ตระบบปฏิบัติการภายในและเสนอ API ข้อความเหตุการณ์อย่างง่ายให้กับผู้ใช้ นอกจากนี้ยังช่วยให้คุณสร้างอะแดปเตอร์สำหรับโปรโตคอลการขนส่งของคุณเองตามกฎบางอย่าง โดยมอบหมายการจัดการอะซิงโครนัสและเธรดที่น่าเบื่อให้กับไลบรารี
หากคุณพบปัญหาในการใช้ห้องสมุดหรือมีความคิดที่จะปรับปรุง อย่าลังเลที่จะเปิดปัญหา ยินดีต้อนรับการมีส่วนร่วมใด ๆ ! และจำไว้ว่า: คาเฟอีนมากขึ้น ประสิทธิผลมากขึ้น!
การจัดการซ็อกเก็ตเป็นเรื่องยากเพราะคุณต้องต่อสู้กับเธรด การทำงานพร้อมกัน ฟูลดูเพล็กซ์ การเข้ารหัส ข้อผิดพลาด IO ที่มาจากระบบปฏิบัติการ (ซึ่งยากต่อการเข้าใจในบางสถานการณ์) ฯลฯ หากคุณใช้ซ็อกเก็ต ที่ไม่บล็อก มันเพิ่มความซับซ้อนอีกชั้นหนึ่ง: ซิงโครไนซ์เหตุการณ์ที่มาจากระบบปฏิบัติการแบบอะซิงโครนัส
message-io
นำเสนอวิธีง่ายๆ ในการจัดการกับปัญหาที่กล่าวมาข้างต้น ทำให้มันโปร่งใสสำหรับคุณ ซึ่งเป็นโปรแกรมเมอร์ที่ต้องการสร้างแอปพลิเคชันที่มีปัญหาของตัวเอง ด้วยเหตุนี้ ไลบรารีจึงให้ API ง่ายๆ แก่คุณโดยมีสองแนวคิดในการทำความเข้าใจ: ข้อความ (ข้อมูลที่คุณส่งและรับ) และ จุดสิ้นสุด (ผู้รับข้อมูลนั้น) นามธรรมนี้ยังเสนอความเป็นไปได้ในการใช้ 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 " ] }
ตัวอย่างต่อไปนี้คือเซิร์ฟเวอร์ที่ง่ายที่สุดที่อ่านข้อความจากไคลเอนต์และตอบกลับด้วยข้อความเดียวกัน สามารถเสนอ "บริการ" สำหรับ 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
มี API สอง ประเภท API ผู้ใช้ ที่สื่อสารกับ message-io
ในฐานะผู้ใช้ไลบรารี และ API ของอะแดปเตอร์ ภายในสำหรับผู้ที่ต้องการเพิ่มอะแดปเตอร์โปรโตคอลของตนลงในไลบรารี
หากสามารถสร้างโปรโตคอลการขนส่งที่ด้านบนของ mio
(ไลบรารีโปรโตคอลที่มีอยู่ส่วนใหญ่สามารถทำได้) คุณสามารถเพิ่มลงใน message-io
ได้อย่างง่ายดาย :
เพิ่มไฟล์ อะแดปเตอร์ ของคุณใน src/adapters/<my-transport-protocol>.rs
ที่ใช้คุณลักษณะที่คุณพบที่นี่ ประกอบด้วยฟังก์ชันบังคับเพียง 8 ฟังก์ชันที่ต้องนำไปใช้ (ดูเทมเพลต) และต้องใช้ประมาณ 150 บรรทัดเพื่อใช้อะแดปเตอร์
เพิ่มฟิลด์ใหม่ใน Transport
enum ที่พบใน src/network/transport.rs เพื่อลงทะเบียนอะแดปเตอร์ใหม่ของคุณ
นั่นคือทั้งหมดที่ คุณสามารถใช้การขนส่งใหม่ของคุณกับ message-io
API ได้เหมือนอย่างอื่น
อ๊ะ! อีกขั้นตอนหนึ่ง: สร้าง Pull Request เพื่อให้ทุกคนสามารถใช้งานได้ :)
message-io
โปรเจ็กต์ที่ยอดเยี่ยมของคุณใช้ message-io
หรือไม่ สร้าง Pull Request และเพิ่มลงในรายการ!
message-io
มีเป้าหมายหลักเพื่อให้สิ่งต่าง ๆ เรียบง่าย นี่เป็นสิ่งที่ดี แต่บางครั้งมุมมองนี้อาจทำให้สิ่งที่ซับซ้อนอยู่แล้วซับซ้อนยิ่งขึ้น
ตัวอย่างเช่น message-io
อนุญาตให้จัดการเหตุการณ์เครือข่ายแบบอะซิงโครนัสโดยไม่ต้องใช้รูปแบบ async/await
จะช่วยลดความซับซ้อนในการจัดการข้อความรายได้จากเครือข่ายซึ่งดีมาก อย่างไรก็ตาม แอปพลิเคชันที่อ่านข้อความอะซิงโครนัสมักจะทำงานแบบอะซิงโครนัสเหนือเหตุการณ์เหล่านี้ด้วย การสืบทอดแบบอะซิงโครนัสนี้สามารถแพร่กระจายไปยังแอปพลิเคชันทั้งหมดของคุณได้อย่างง่ายดาย ซึ่งยากต่อการบำรุงรักษาหรือปรับขนาดโดยไม่มีรูปแบบอะซิงโครนัส/รอ ในกรณีเหล่านั้น บางที tokio
อาจเป็นตัวเลือกที่ดีกว่า คุณต้องจัดการกับสิ่งต่าง ๆ ของเครือข่ายระดับต่ำ แต่คุณจะได้รับการจัดการองค์กรและเธรด / ทรัพยากร
ปัญหาที่คล้ายกันอาจเกิดขึ้นได้เกี่ยวกับการใช้งานโหนดของ message-io
เนื่องจากโหนดสามารถใช้งานได้อย่างอิสระในฐานะไคลเอ็นต์/เซิร์ฟเวอร์ หรือทั้งสองอย่าง คุณจึงสามารถเริ่มสร้างแอปพลิเคชันแบบเพียร์ทูเพียร์ได้อย่างง่ายดาย ที่จริงแล้วนี่คือหนึ่งในความตั้งใจของ message-io
อย่างไรก็ตาม หากเป้าหมายของคุณขยายขนาด ปัญหาที่เกี่ยวข้องกับรูปแบบนี้จะปรากฏขึ้นเพื่อจัดการ และไลบรารีเช่น libp2p
มาพร้อมกับแบตเตอรี่เครื่องมือจำนวนมากที่จะช่วยเก็บถาวรเป้าหมายนั้น
แน่นอนว่านี่ไม่ใช่การปฏิเสธความรับผิดชอบเกี่ยวกับการใช้งานห้องสมุด (ฉันใช้มัน!) แต่เป็นเรื่องเกี่ยวกับความซื่อสัตย์เกี่ยวกับความสามารถของห้องสมุดมากกว่า และเพื่อแนะนำคุณเกี่ยวกับเครื่องมือที่เหมาะสม ขึ้นอยู่กับสิ่งที่คุณกำลังมองหา
สรุป:
message-io
!tokio
, libp2p
หรืออื่นๆ เพื่อให้สามารถควบคุมได้มากขึ้น