69个版本 (13个稳定版)
1.1.7 | 2023年11月26日 |
---|---|
1.1.6 | 2021年3月12日 |
1.1.5 | 2020年9月15日 |
1.1.2 | 2020年2月26日 |
0.4.1 | 2017年2月9日 |
在 网络编程 中排名 #1030
每月下载量 200
用于 2 crate
99KB
2K SLoC
mles-rs
Mles (现代轻量级通道服务) 是一个客户端-服务器数据分发协议,旨在作为轻量级和可靠的分布式发布-订阅数据服务。参考实现包括 Mles-utils、Mles 服务器和 Mles-client/WebSocket代理。
请访问 https://mles.io 和 https://mles.io/blog.html 以获取Mles的通用概述,以及 /c/mles 以获取最新消息。
注意:Mles版本1.X已弃用,并将在2024年开始废弃。请考虑升级到Mles v2。请访问 /c/mles 查看讨论和详细信息!
Mles 1.0协议概述
客户端使用Mles协议头和TCP或TLS会话中的(uid, channel, message)值三元组连接到Mles服务器。三元组以CBOR(紧凑二进制对象表示)[1]的形式封装。Mles客户端首先通过发送正确的Mles协议头和值三元组(uid, channel, msg)订阅频道,其中channel是要发布/订阅的频道。消息可以是空的,也可以包含第一条发布的消息。Mles服务器验证Mles协议头,然后将Mles客户端加入所选频道。每个频道都使用自己的上下文,与其他频道独立:因此,对于每个(uid, channel)对,始终使用TCP/TLS会话。加入后,Mles服务器将值三元组分发给同一频道上的所有客户端。如果Mles客户端想要离开频道,只需关闭TCP/TLS会话。如果Mles客户端不想接收任何消息,只需关闭TCP/TLS会话的接收端。默认情况下,应选择TLS会话作为会话类型。
Mles服务器和客户端之间的每个会话都使用64位SipHash [2]进行认证。64位密钥在提供的UTF-8字符串上散列。这些可以从用户连接端点详情和/或共享密钥以及会话(uid, channel)值组合。这允许Mles服务器验证连接的Mles客户端确实从它声称的端点连接,并具有适当用户和频道详情。位于网络地址转换(NAT)后面的Mles客户端会话必须使用不带会话端点详情认证的共享密钥。
Mles服务器在认证会话并将连接的Mles客户端移动到其频道上下文之后,应忽略SipHash密钥。上下文更改后,SipHash密钥可以更改并在频道上下文中由Mles客户端使用。
Mles服务器可以联系到Mles对等服务器。Mles对等服务器将此会话视为另一个Mles客户端会话。这允许Mles服务器以组织化、强大且简单的方式在Mles服务器之间共享和分发值三元组数据。
每个连接还有一个32位连接ID(CID),应等于SipHash密钥的最低4个字节。如果不使用SipHash密钥作为值,CID必须以确定性的方式进行配置。如果Mles服务器在同一个频道上的活动连接中具有CID,它必须丢弃具有相同CID的进一步传入连接。这允许在配置成循环拓扑的端点服务器中实现有效自主循环保护。
Mles服务器可以配置为具有静态CID值,并使用对等服务器配置创建负载均衡/保护组。组中的所有成员必须配置相同的静态CID。该组也可以是一圈服务器。
Mles服务器可以保存接收到的数据的历史记录,然后在客户端连接到Mles服务器时将其分发给新客户端。如果Mles服务器重新启动,Mles对等服务器,甚至Mles客户端,可以提供历史数据作为Mles服务器的重同步,以用于它具有历史记录的频道。这允许对频道信息进行分布式数据保护。
Mles客户端和服务器独立于IP版本,不使用IP广播或多播。Mles服务器可以配置为使用IP任播。
Mles客户端可以实现WebSocket [4]代理,这允许通过WebSocket协议进行Mles连接。参考客户端中提供了这种简单代理实现的示例。
Mles协议已注册为使用Internet号码分配机构(IANA)端口8077 [3]。
Mles协议详情
Mles的协议头如下
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ASCII 'M'(77) | Encapsulated data length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CID (lowest 4-bytes of initial SipHash) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ SipHash +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CBOR encapsulated data (uid, channel, message) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
CBOR封装的数据安排如下
uid: Major type 3, UTF-8 String
channel: Major type 3, UTF-8 String
message: Major type 2, byte string
使用Rust,上面的代码如下所示
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Msg {
uid: String,
channel: String,
#[serde(with = "serde_bytes")]
message: Vec<u8>,
}
当端点详情(IPv4或IPv6地址)作为CID的一部分使用时,UTF8字符串格式如下
- IPv4:
a.b.c.d:端口号
- IPv6:
[a:b::..::c:d]:端口号
ResyncMsg,用于向Mles服务器共享历史信息,采用CBOR格式
resync_message: Major type 2, byte string
在Rust中定义如下
#[derive(Serialize, Deserialize, Debug, Clone)]
struct MsgVec {
#[serde(with = "serde_bytes")]
encoded_msg: Vec<u8>, //this is encoded Msg
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ResyncMsg {
resync_message: Vec<MsgVec>,
}
Mles WebSocket协议详情
一个带有WebSocket代理的Mles客户端允许使用WebSocket协议[4]连接到Mles代理,然后代理将通过WebSocket协议将帧从Mles网络转发。Mles WebSocket代理必须能够无Mles协议头地传输CBOR封装的帧,如Mles协议详情部分所述。此外,代理应能够通过WebSocket在同一个连接上传输多个通道。为了使代理能够在转发帧之上添加新的安全层,应在实际数据负载之前,为每个Msg
初始化16字节的安全随机数据。带有WebSocket代理的Mles客户端将处理Mles协议头的添加和删除。连接到代理的Mles WebSocket客户端可以很容易地在现代浏览器上实现,例如使用JavaScript及其现有的CBOR库。
Mles WebSocket客户端必须设置Sec-WebSocket-Protocol子协议为"mles-websocket"[5],才能成功连接到带有WebSocket代理的Mles客户端。
用法
参考二进制文件
mles [peer-address] [--history-limit=N]
mles-client <server-address> [--use-websockets]
NAT后面Mles连接的共享密钥设置(此密钥在mles.io中默认使用)
MLES_KEY=mles-devel-frank mles [peer-address] [--history-limit=N]
MLES_KEY=mles-devel-frank mles-client <server-address> [--use-websockets]
要使用mles-utils
API,首先将其添加到您的Cargo.toml
[dependencies]
mles-utils = "1.0"
接下来,将其添加到您的crate中
extern crate mles_utils;
use mles_utils::*;
fn main() {
// ...
}
客户端API示例
extern crate mles_utils;
use std::net::SocketAddr;
use std::thread;
use mles_utils::*;
fn main() {
let child = thread::spawn(move || {
//set server address to connect
let addr = "127.0.0.1:8077".parse::<SocketAddr>().unwrap();
//set channel
let channel = "Channel".to_string();
//set user
let uid = "Receiver".to_string();
//connect client to server
let mut conn = MsgConn::new(uid, channel);
conn = conn.connect(addr);
//blocking read for hello world
let (conn, msg) = conn.read_message();
let msg = String::from_utf8_lossy(msg.as_slice());
assert_eq!("Hello World!", msg);
println!("Just received: {}", msg);
conn.close();
});
let addr = "127.0.0.1:8077".parse::<SocketAddr>().unwrap();
let uid = "Sender".to_string();
//set matching channel
let channel = "Channel".to_string();
//set message
let message = "Hello World!".to_string();
//send hello world to awaiting client
let mut conn = MsgConn::new(uid, channel);
conn = conn.connect_with_message(addr, message.into_bytes());
conn.close();
//wait receiver and return
let _res = child.join();
}
基于Mles(WebSocket)协议的现有客户端实现
- 参考客户端(Rust语言)
- mles-webproxy:基于Warp的Rust语言TLS和多功能Web代理
- MlesTalk(JavaScript语言)
- <请在此处添加您的客户端!>
参考文献
- 传输层安全性(TLS)协议版本1.3,https://tools.ietf.org/html/rfc8446
- 紧凑二进制对象表示(CBOR),https://tools.ietf.org/html/rfc7049
- SipHash:快速短输入PRF,https://131002.net/siphash/,参考日期:2017年4月2日
- IANA注册的Mles端口号8077,http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=8077
- WebSocket协议,https://tools.ietf.org/html/rfc6455
- IANA注册的Mles WebSocket子协议,https://www.iana.org/assignments/websocket
依赖项
~5MB
~85K SLoC