message-io
— это быстрая и простая в использовании сетевая библиотека, управляемая событиями. Библиотека внутренне обрабатывает сокет ОС и предлагает пользователю простой API сообщений о событиях. Он также позволяет вам создать адаптер для вашего собственного транспортного протокола, следуя некоторым правилам, делегируя утомительное управление асинхронностью и потоками библиотеке.
Если вы обнаружили проблему при использовании библиотеки или у вас есть идеи по ее улучшению, не стесняйтесь открыть проблему. Любой вклад приветствуется! И помните: чем больше кофеина, тем продуктивнее!
Управлять сокетами сложно, потому что вам нужно бороться с потоками, параллелизмом, полнодуплексным режимом, кодированием, ошибками ввода-вывода, исходящими от ОС (которые в некоторых ситуациях действительно сложно понять) и т. д. Если вы используете неблокирующие сокеты, это добавляет новый уровень сложности: синхронизируйте события, поступающие асинхронно от операционной системы.
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 " ] }
Следующий пример представляет собой простейший сервер, который читает сообщения от клиентов и отвечает им тем же сообщением. Он может предлагать «услугу» для трех разных протоколов одновременно.
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
в src/network/transport.rs, чтобы зарегистрировать новый адаптер.
Вот и все. Вы можете использовать свой новый транспорт с API-интерфейсом message-io
как и любой другой.
Упс! еще один шаг: сделайте запрос на включение , чтобы каждый мог его использовать :)
message-io
Ваш потрясающий проект использует message-io
? Сделайте запрос на включение и добавьте его в список!
Основная цель message-io
— упростить задачу. Это здорово, но иногда такая точка зрения может усложнить и без того сложные вещи.
Например, message-io
позволяет обрабатывать асинхронные сетевые события без использования шаблона async/await
. Это упрощает обработку сообщений о доходах из сети, и это здорово. Тем не менее, приложения, читающие асинхронные сообщения, имеют тенденцию выполнять асинхронные задачи и над этими событиями. Это асинхронное наследование можно легко распространить на все приложение, которое сложно поддерживать или масштабировать без шаблона async/await. В таких случаях, возможно, tokio
будет лучшим вариантом. Вам придется иметь дело с более низкоуровневыми сетевыми задачами, но вы выиграете в организации и управлении потоками/ресурсами.
Аналогичная проблема может возникнуть при использовании узла message-io
. Поскольку узел можно использовать независимо как клиент/сервер или и то, и другое, вы можете легко начать создавать одноранговые приложения. Фактически, это одна из целей message-io
. Тем не менее, если ваша цель масштабируется, возникнут проблемы, связанные с этим шаблоном, и такие библиотеки, как libp2p
поставляются с огромным набором инструментов, которые помогут заархивировать эту цель.
Конечно, речь идет не об отказе от использования библиотеки (я ею пользуюсь!), а скорее о том, чтобы честно рассказать о ее возможностях и направить вас к правильному инструменту в зависимости от того, что вы ищете.
Подводя итог:
message-io
!tokio
, libp2p
или другие, чтобы иметь больше контроля над ней.