59个版本 (13个稳定版本)
1.1.7 | 2023年11月26日 |
---|---|
1.1.6 | 2021年3月12日 |
1.1.5 | 2020年9月16日 |
1.1.2 | 2020年2月26日 |
0.5.0 | 2017年3月25日 |
#859 in 网络编程
每月113次下载
135KB
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协议头和(uid, channel, message)值三元组通过TCP或TLS[1]会话连接到Mles服务器,其中三元组是简明二进制对象表示(CBOR)[1]封装的。Mles客户端首先通过发送正确的Mles协议头和值三元组(uid, channel, msg)订阅通道,其中channel是发布/订阅的通道。消息msg可以是空的,也可以包括第一条发布消息。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客户端确实是从它声称的连接端点连接的,并带有适当用户和通道细节。在Network Address Translation (NAT)之后的Mles客户端会话必须使用不带会话端点细节认证的共享密钥。
Mles服务器认证会话并将连接的Mles客户端移动到其通道上下文后,SipHash密钥应被Mles服务器忽略。在上下文更改后,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 Assigned Number Authority (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:port
- IPv6:
[a:b::..::c:d]:port
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封装的帧。此外,代理应该能够在同一个连接上通过WebSocket传输多个通道。为了使代理能够在转发帧上添加新的安全层,message
部分应该始终在每个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 TLS和多通道Web代理,使用Rust在Warp上实现
- MlesTalk(JavaScript语言实现)
- <请在此处添加您的客户端!>
参考文献
- 传输层安全性(TLS)协议版本1.3,https://tools.ietf.org/html/rfc8446
- 简洁二进制对象表示(CBOR),https://tools.ietf.org/html/rfc7049
- SipHash:一种快速短输入PRF,https://131002.net/siphash/,参考日期4.2.2017
- 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
依赖项
~11MB
~208K SLoC