|
|
# Mux.Cool 协议
|
|
|
|
|
|
Mux.Cool 协议是一个多路复用传输协议,用于在一条已建立的数据流中传输多个各自独立的数据流。
|
|
|
|
|
|
## 版本
|
|
|
|
|
|
当前版本是 1 Beta。
|
|
|
|
|
|
## 依赖
|
|
|
|
|
|
### 底层协议
|
|
|
|
|
|
Mux.Cool 必须运行在一个已建立的可靠数据流之上。
|
|
|
|
|
|
## 通讯过程
|
|
|
|
|
|
一个 Mux.Cool 连接中可传输若干个子连接,每个子连接有一个独立的 ID 和状态。传输过程由帧(Frame)组成,每一帧用于传输一个特定的子连接的数据。
|
|
|
|
|
|
### 客户端行为
|
|
|
|
|
|
当有连接需求时并且没有现有可用的连接时,客户端向服务器发起一个新连接,以下称为“主连接”。
|
|
|
|
|
|
1. 一个主连接可用于发送若干个子连接。客户端可自主决定主连接可承载的子连接数量。
|
|
|
1. 对于一个新的子连接,客户端必须发送状态`New`以通知服务器建立子连接,然后使用状态`Keep`来传送数据。
|
|
|
1. 当子连接结束时,客户端发送`End`状态来通知服务器关闭子连接。
|
|
|
1. 客户端可自行决定何时关闭主连接,但必须确定服务器也同时保持连接。
|
|
|
1. 客户端可使用 KeepAlive 状态来避免服务器关闭主连接。
|
|
|
|
|
|
### 服务器端行为
|
|
|
|
|
|
当服务器端接收到新的子连接时,服务器应当按正常的连接来处理。
|
|
|
|
|
|
1. 当收到状态`End`时,服务器端可以关闭对目标地址的上行连接。
|
|
|
1. 在服务器的响应中,必须使用与请求相同的 ID 来传输子连接的数据。
|
|
|
1. 服务器不能使用`New`状态。
|
|
|
1. 服务器可使用 KeepAlive 状态来避免客户端关闭主连接。
|
|
|
|
|
|
## 传输格式
|
|
|
|
|
|
Mux.Cool 使用对称传输格式,即客户端和服务器发送和接收相同格式的数据。
|
|
|
|
|
|
### 帧格式
|
|
|
|
|
|
| 2 字节 | L 字节 | X 字节 |
|
|
|
| ------------ | ------ | -------- |
|
|
|
| 元数据长度 L | 元数据 | 额外数据 |
|
|
|
|
|
|
### 元数据
|
|
|
|
|
|
元数据有若干种类型。所有类型的元数据都包含 ID 和 Opt 两项,其含义为:
|
|
|
|
|
|
- ID: 子连接的唯一标识
|
|
|
- 对于一般 Mux 子连接,ID 由 1 开始累加
|
|
|
- 对于 Xray 实现的 [Single XUDP](https://github.com/XTLS/Xray-core/blob/main/common/xudp/xudp.go),ID 始终为 0
|
|
|
- Opt:
|
|
|
- D(0x01): 有额外数据
|
|
|
|
|
|
当选项 Opt(D) 开启时,额外数据格式如下:
|
|
|
|
|
|
| 2 字节 | X-2 字节 |
|
|
|
| -------- | -------- |
|
|
|
| 长度 X-2 | 数据 |
|
|
|
|
|
|
### 新建子连接 (New)
|
|
|
|
|
|
| 2 字节 | 1 字节 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | A 字节 | 8 字节 |
|
|
|
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ | ---------------- |
|
|
|
| ID | 0x01 | 选项 Opt | 网络类型 N | 端口 | 地址类型 T | 地址 A | Global ID (XUDP) |
|
|
|
|
|
|
其中:
|
|
|
|
|
|
- 网络类型 N:
|
|
|
- 0x01:TCP,表示当前子连接的流量应当以 TCP 的方式发送至目标。
|
|
|
- 0x02:UDP,表示当前子连接的流量应当以 UDP 的方式发送至目标。
|
|
|
- 地址类型 T:
|
|
|
- 0x01:IPv4
|
|
|
- 0x02:域名
|
|
|
- 0x03:IPv6
|
|
|
- 地址 A:
|
|
|
- 当 T = 0x01 时,A 为 4 字节 IPv4 地址;
|
|
|
- 当 T = 0x02 时,A 为 1 字节长度(L) + L 字节域名;
|
|
|
- 当 T = 0x03 时,A 为 16 字节 IPv6 地址;
|
|
|
- Global ID (XUDP):
|
|
|
- 客户端计算出 UDP 来源二元组的全局独特 ID,服务端用以确保当 XUDP 断线重连时,仍使用同一个端口与目标通信。
|
|
|
|
|
|
在新建子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
|
|
|
|
|
|
### 保持子连接 (Keep)
|
|
|
|
|
|
TCP
|
|
|
|
|
|
| 2 字节 | 1 字节 | 1 字节 |
|
|
|
| ------ | ------ | -------- |
|
|
|
| ID | 0x02 | 选项 Opt |
|
|
|
|
|
|
UDP
|
|
|
|
|
|
| 2 字节 | 1 字节 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | A 字节 |
|
|
|
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ |
|
|
|
| ID | 0x02 | 选项 Opt | 网络类型 N | 端口 | 地址类型 T | 地址 A |
|
|
|
|
|
|
在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
|
|
|
XUDP 在 Opt(D) 之后加 UDP 地址,格式同新建子连接,但没有 Global ID。
|
|
|
|
|
|
### 关闭子连接 (End)
|
|
|
|
|
|
| 2 字节 | 1 字节 | 1 字节 |
|
|
|
| ------ | ------ | -------- |
|
|
|
| ID | 0x03 | 选项 Opt |
|
|
|
|
|
|
在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
|
|
|
|
|
|
### 保持连接 (KeepAlive)
|
|
|
|
|
|
| 2 字节 | 1 字节 | 1 字节 |
|
|
|
| ------ | ------ | -------- |
|
|
|
| ID | 0x04 | 选项 Opt |
|
|
|
|
|
|
在保持连接时:
|
|
|
|
|
|
- 若 Opt(D) 开启,则这一帧所带的数据必须被丢弃。
|
|
|
- ID 可为随机值。
|
|
|
|
|
|
## 应用
|
|
|
|
|
|
Mux.Cool 协议与底层协议无关,理论上可以使用任何可靠的流式连接来传输 Mux.Cool 的协议数据。
|
|
|
|
|
|
在目标导向的协议如 Shadowsocks 和 VMess 协议中,连接建立时必须包含一个指定的地址。
|
|
|
为了保持兼容性,Mux.Cool 协议指定地址为“v1.mux.cool”。即当主连接的目标地址与之匹配时,则进行 Mux.Cool 方式的转发,否则按传统方式进行转发。(注:这是一个程序内的标记,VMess 和 VLESS 并不会在数据包中发送“v1.mux.cool”地址)
|