53 次发布
0.18.2 | 2024年7月11日 |
---|---|
0.18.1 | 2024年2月2日 |
0.18.0 | 2023年9月13日 |
0.17.0 | 2023年5月17日 |
0.2.0 | 2020年7月15日 |
#12 in WebSocket
1,140 下载/月
用于 5 crates
205KB
3.5K SLoC
message-io
是一个快速易用的基于事件的网络库。该库内部处理操作系统套接字,并为用户提供简单的消息事件 API。它还允许您根据一些 规则 为自己的传输协议创建适配器,将繁琐的异步性和线程管理委托给库。
如果您在使用库时发现问题或您有改进它的想法,请毫不犹豫地提出问题。 任何贡献都受欢迎! 并且请记住:更多的 咖啡因,更有效率!
动机
管理套接字很困难,因为你需要与线程、并发、全双工、编码、来自操作系统的 IO 错误(在某些情况下这些错误非常难以理解)等进行斗争。如果您使用非阻塞套接字,它还会增加一个新的复杂性层:同步来自操作系统的异步事件。
message-io
提供了一种轻松处理所有这些问题的方法,使这些问题对您(想要使用其自身问题创建应用程序的程序员)来说变得透明。为此,库为您提供了一个简单的 API,其中包含两个易于理解的概念:**消息**(您发送和接收的数据)和**端点**(数据的接收者)。这种抽象还提供了使用相同 API 独立于使用的传输协议的可能性。您可以在 literally 一行内更改应用程序的传输方式。
特性
- 高度可扩展:允许管理数千个活动连接的**非阻塞套接字**。
- 多平台:查看 mio 平台支持。
- 多种传输协议 (文档)
- TCP:流模式和帧模式(用于处理消息而非流)
- UDP,带有多播选项
- WebSocket:普通和#102选项,使用tungstenite-rs(不支持
wasm
,但计划中)。
- 带有计时器和优先级的自定义FIFO事件
- 简单、直观且一致的API
- 遵循KISS原则。
- 从传输层抽象:不要考虑套接字,考虑消息和端点。
- 仅使用两个主要实体
- 一个
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"] }
一站式:TCP、UDP和WebSocket回显服务器
以下示例是最简单的服务器,它从客户端读取消息并对它们进行响应。它能够同时为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
的开源项目
- Termchat 通过局域网进行视频流和文件传输的终端聊天。
- Egregoria 沉思社会模拟。
- Project-Midas 基于分布式网络的并行计算系统。
- AsciiArena 终端多人死亡比赛游戏(alpha)。
- LanChat LanChat flutter + rust 示例。
您的出色项目是否使用 message-io
?提交一个 Pull Request 并将其添加到列表中!
message-io 适合我吗?
message-io
的主要目标是保持简单。这很好,但有时这种观点可能会使本来已经复杂的事情变得更复杂。
例如,message-io
允许在不需要 async/await
模式的情况下处理异步网络事件。这简化了处理来自网络的收入消息的复杂性,这是非常好的。尽管如此,读取异步消息的应用程序往往会在这类事件上执行异步任务。这种异步继承很容易传播到您的整个应用程序,在没有 async/await 模式的情况下很难维护或扩展。在这些情况下,也许 tokio
是更好的选择。您需要处理更多底层的网络事物,但您在组织和线程/资源管理方面获得了优势。
关于 message-io
的节点使用,也可能出现类似的问题。因为节点可以作为独立的客户端/服务器或两者同时使用,所以您可以很容易地开始制作对等网络应用程序。事实上,这是 message-io
的一个意图之一。尽管如此,如果您的目标需要扩展,将出现与这种模式相关的问题,而像 libp2p
这样的库提供了大量工具来帮助实现这一目标。
当然,这并非是对库使用的否认(我用它!),更多的是关于对其能力的诚实描述,以及根据您的需求引导您使用正确的工具。
总结如下:
依赖项
~2–10MB
~113K SLoC