message-io
는 빠르고 사용하기 쉬운 이벤트 중심 네트워크 라이브러리입니다. 라이브러리는 내부적으로 OS 소켓을 처리하고 사용자에게 간단한 이벤트 메시지 API를 제공합니다. 또한 몇 가지 규칙에 따라 자체 전송 프로토콜용 어댑터를 만들어 지루한 비동기 및 스레드 관리를 라이브러리에 위임할 수도 있습니다.
라이브러리를 사용하면서 문제점을 발견하거나 개선할 아이디어가 있다면 주저하지 말고 이슈를 열어주세요. 어떤 기여라도 환영합니다! 그리고 기억하세요: 카페인을 더 많이 섭취하면 생산성도 높아집니다!
스레드, 동시성, 전이중, 인코딩, OS에서 발생하는 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를 제공합니다. 라이브러리 사용자로서 message-io
자체와 통신하는 사용자 API 와 프로토콜 어댑터를 라이브러리에 추가하려는 사용자를 위한 내부 어댑터 API 입니다.
mio
위에 전송 프로토콜을 구축할 수 있다면(대부분의 기존 프로토콜 라이브러리가 가능) 이를 message-io
에 정말 쉽게 추가할 수 있습니다.
여기에서 찾은 특성을 구현하는 src/adapters/<my-transport-protocol>.rs
에 어댑터 파일을 추가하세요. 여기에는 구현할 필수 기능이 8개만 포함되어 있으며(템플릿 참조) 어댑터를 구현하는 데 약 150줄이 소요됩니다.
src/network/transport.rs에 있는 Transport
열거형에 새 필드를 추가하여 새 어댑터를 등록하세요.
그게 다야. 다른 것과 마찬가지로 message-io
API를 통해 새로운 전송을 사용할 수 있습니다.
이런! 한 단계 더: 모든 사람이 사용할 수 있도록 Pull Request 를 만드세요 :)
message-io
사용한 오픈소스 프로젝트 당신의 멋진 프로젝트는 message-io
사용하나요? Pull Request를 작성하고 목록에 추가하세요!
message-io
일을 단순하게 유지하는 주요 목표를 가지고 있습니다. 이것은 훌륭하지만 때로는 이러한 관점이 이미 복잡한 일을 더 복잡하게 만들 수 있습니다.
예를 들어 message-io
사용하면 async/await
패턴을 사용하지 않고도 비동기 네트워크 이벤트를 처리할 수 있습니다. 네트워크의 수입 메시지를 처리하는 복잡성이 줄어듭니다. 이는 훌륭한 일입니다. 그럼에도 불구하고 비동기 메시지를 읽는 애플리케이션은 이러한 이벤트에 대해서도 비동기 작업을 수행하는 경향이 있습니다. 이 비동기 상속은 비동기/대기 패턴 없이 유지 관리하거나 확장하기 어려운 전체 애플리케이션에 쉽게 전파될 수 있습니다. 그런 경우에는 tokio
더 나은 선택이 될 수 있습니다. 더 낮은 수준의 네트워크 작업을 처리해야 하지만 조직 및 스레드/리소스 관리 측면에서 이점을 얻을 수 있습니다.
message-io
의 노드 사용과 관련하여 유사한 문제가 발생할 수 있습니다. 노드는 독립적으로 클라이언트/서버 또는 둘 다로 사용될 수 있으므로 P2P 애플리케이션 만들기를 쉽게 시작할 수 있습니다. 사실 이것은 message-io
의 의도 중 하나입니다. 그럼에도 불구하고 목표가 확장되면 처리해야 할 패턴과 관련된 문제가 나타날 것이며 libp2p
와 같은 라이브러리에는 해당 목표를 보관하는 데 도움이 되는 막대한 도구 배터리가 제공됩니다.
물론 이는 라이브러리 사용에 대한 부인이 아니라(저는 사용합니다!) 라이브러리의 기능에 대해 솔직하게 설명하고 찾고 있는 항목에 따라 올바른 도구를 안내하는 것입니다.
요약하자면:
message-io
사용하여 더 간단하게 만드세요!tokio
, libp2p
등을 사용하여 더 효과적으로 제어할 수 있습니다.