本文是对 LibP2P 官网概念文章的翻译以及资料补充,同时有笔者的一些思考注释。
TRANSPORT
文章链接:docs.libp2p.io/concepts/tr…
参考翻译:zhuanlan.zhihu.com/p/461442975
当你的计算机连接到互联网上的其他计算机时,大可能使用的是TCP/IP协议来传输数据。TCP/IP协议是互联网协议和传输控制协议的成功组合,能够高效处理数据包的寻址和发送,同时确保网络传输的信息能够被正确和完整地接收。
因为 TCP/IP 应用非常普遍,而且得到了众多厂商的支持,所以它通常是网络应用程序的默认选择。然而,在某些情况下,TCP协议会带来不小的开销,所以有些应用程序也可能使用 UDP;一个更简单的协议,但不保证可靠性和接收顺序。
尽管 TCP 和 UDP(以及IP)是目前最为广泛使用的协议,但绝非是唯一的协议。就网络分层来看,其他替代方案的协议包括:更底层的协议(发送原始的以太帧或者是蓝牙信号)、更高层的协议(比如QUIC.他在UDP协议的上层)。
相较网络分层来说的协议:
- 工作在网络层(Network Layer)的协议 --> Internet Protocol 也就是IP 协议
- 工作在传输层(Transport Layer)的协议 --> Transport Protocol 也就是传输控制协议(TCP,UDP) 这里只指传输协议带来的开销,而更下层指的是Mac层,物理层的协议;更上层当然就是应用层了 协议分层速查: 阮一峰教程
在LibP2P看来,以上这些在通信链路上传输 bits 的基本协议额称为基础协议。不过,libp2p 的核心目标之一是与底层的传输协议解耦。这意味着使用何种传输协议可由开发人员决定,事实上,一个应用程序可以同时支持许多不同的传输协议。
而本文所讲的 Transport 可理解为定义的传输动作。
监听和拨号
Transport(传输)可以定义为两个核心的操作: 监听(Listening) 和 拨号 (Dialing)
-
Listening 监听: 意味着可以使用 传输层 提供的各种接口,接收来自其他对等点(指的是网络分层当中处于同一层)的 “拨号” 请求。例如,unix 操作系统上可以使用 bind 和 listen 系统函数调用进行 TCP 监听,“监听” 进程会将指定端口上的数据包路由后发送到应用程序中。
我理解就是创建一个Client进程,接收数据
-
Dialing 拨号: 是打开一个到正在“监听”的对等点的连接的过程。与“监听”一样,细节是由实现决定的,但 libp2p 实现中的每个 Transport 都将共享相同的编程接口。
我理解就是创建一个Request进程,发送请求
地址
在拨通一个对等点或者是建立连接之前,首先需要知道如何与它们取得联系。因为每个传输协议都会有自己的地址形式和定义,所以 libp2p 使用称为“ multiaddress”或 multiaddr 的约定来编码许多不同的地址模式。
addressing doc对 multiaddress 的讲解更详细,以下会对地址做一些介绍:
下面是 TCP/IP Transport 的 multiaddress 示例:
/ip4/7.7.7.7/tcp/6543
# 分成两部分来看 /ip4/7.7.7.7 和 /tcp/6543
相比较现在常用的地址形式:7.7.7.7:6542 (IP:Port 的二元组结构),multiaddress 能够更加明确的对协议进行描述。用 multiaddress 表示的地址可以一目了然地看出 7.7.7.7 属于 IPv4 协议,6543 是一个 TCP 端口。
“拨号”和“监听”都会对 multiaddress 进行处理。 监听时,需要为 Transport 提供想监听的 multiaddress,拨号时提供要拨号的 multiaddress。
当对远程对等点拨号时,multiaddress 中应该要包含对象的地址(在本文所讲的 LibP2P条件下表示为 PeerId,本质就是一个multiaddress,就是上述所给的地址表示方式)。在 libp2p 的世界中,PeerId 用于建立一个安全的信道并防止别人冒充。
包含 PeerId 的 multiaddress 示例:
/ip4/1.2.3.4/tcp/4321/p2p/QmcEPrat8ShnCph8WjkREzt5CPXF2RwhYxYBALDcLC1iV6
/p2p/QmcEPrat8ShnCph8WjkREzt5CPXF2RwhYxYBALDcLC1iV6 表示使用公钥的加密后,用于标识唯一的一个远程对等点ID(有关这个P2P 的ID 表示以及定义内容 ,后面会提到也可以直接看原文。
Tip
启用 peer routing 后,就可以仅使用对等点的 PeerId 向对等点进行拨号,而无需事先知道它们的通信地址。换句话说:是不是这个ID 可以将之前的那些协议地址都包含进来,也就是说解析之后会呈现出
/ip4/1.2.3.4/tcp/4321形式?(待解答)
多传输
基于 libp2p 的应用程序通常需要同时支持多种 Transport (这个应该指的是协议以及并发)。 例如,当某个的服务在与某个进程建立 TCP 连接的同时,还能接受来自互联网中其他对等点的 Websocket 连接。
负责管理 Transport 的 libp2p 组件称为 switch ,它还负责处理 协议协商、流复用、建立安全通道以及各种形式的“连接升级”,这些内容后面的翻译都会有。
switch 为 “拨号” 和 “监听” 提供了一个 “入口点” ,使应用程序开发者不必操心底层使用的具体传输协议以及“网络连接堆栈”等复杂的部分。
“swarm” 以前用来指现在称为 “switch” 的东西,代码库中的一些地方仍然使用 “swarm” 术语。
Switch : 一个 libp2p 组件,负责将多个 Transport 组合成单个接口,允许应用程序代码 Dialing 对等点,而无需指定使用哪一个 Transport 。
除了管理 Transport 之外,交换机还协调“连接升级”过程,该过程促进从传输层到支持协议协商、流多路复用和安全通信的“原始”连接。
NAT TRAVERSAL
互联网是由无数的 网络 组成的,通过基本的传输协议将这些网络连接在一起,形成共享的地址空间。
当流量(数据流)在网络边界之间移动时,通常会出现一个名为“网络地址转换”的过程。网络地址转换(NAT)将地址从一个地址空间映射到另一个地址空间。
NAT 允许许多机器共享一个公共地址,这对于 IPv4协议 的持续运行至关重要,否则仅凭 IPV4 的 32位地址空间 是远远不够满足现代网络人口的需求的。
例如,当我连接到我的家庭 wifi 时,我的计算机得到的 IPv4地址 是 10.0.1.15。这是 专用网络 内部使用的一系列 IP 地址的一部分。当我本地的计算机向外连接到一个公共 IP 地址时,本地的路由器用它自己的 公共 IP 地址 替换我的内部 IP。同时当数据从外部的另一端返回我本地的电脑时,这个路由器将转换回内部的地址。
IP 地址分为公网地址和私有地址。
- 公网地址有 IANA 统一分配,用于连接互联网;
- 私有地址可以自由分配,用于私有网络内部通信。 复习资料: 42张图详解 NAT : 换个马甲就能上网,公网IP与内网IP的区别,NAT 穿透原理浅浅说
虽然 NAT 对于传出连接通常是透明的(意思是我本地是不知道传出去的地址是多少的,同时另一端也只能看到NAT之后的地址而不是我内部的地址),但是侦听传入连接需要一些配置。这里举一个例子:本地路由器监听一个公共 IP 地址,期望内部网络上的任何数量的计算机都可以处理这个请求;为了实现这个功能设置,本地路由器必须被配置为向特定的机器发送特定的流量,通常是通过将一个或多个 TCP 或 UDP 端口 从公共 IP 映射到内部端口。
虽然通常可以手动配置路由器,但并不是每个想要运行对等应用程序或其他网络服务的人都有能力这样做。
设计者希望 libp2p 应用程序可以在任何地方运行,而不仅仅是在数据中心或具有稳定公共 IP 地址的机器上。为了实现这一点,以下是目前 libp2p 中可用的 NAT 遍历的主要方法。
Automatic router configuration
许多路由器支持端口转发的自动配置协议,最常见的是 UPnP 或 nat-pmp。
DHCP
如果路由器支持某一种自动配置的协议,libp2p 将尝试自动配置一个端口映射,以允许它侦听传入流量。如果有对应的 Network 和 libp2p 接口支持,这通常是最简单的选项。
Hole-punching (STUN)
当一台内部机器 “Dialing Out” 并连接到一个公共地址时,路由器将把一个公共端口映射到用于连接的内部 IP 地址。与此相对应的,当外部连接(访问)传入该端口时,路由器会将它们路由到相同的内部地址(机器)。
Libp2p 将在使用 IP 支持的传输时尝试利用这种行为,方法是使用同一个端口进行 Dialing 和 Listening ,并使用一个名为 SO _ REUSEPORT 的套接字选项。
同时即便与内部机器连接的对等方(Peer)能处于一个有利的网络环境中,在建立一个对外连接时候可以“免费”获得一个公共可到达的监听端口,但这个对等方机器 (Peer)可能永远不会知道这一点,也就是说,拨号程序无法自行发现分配给连接的端口。
尽管如此,外部连接的机器是能够告知内部他们所看到的地址的,然后内部的机器就可以将这个地址广播给其他的外部机器,来让他们建立与内部机器的连接。
举例来说:PeerA Dial PeerB 并建立了连接。PeerB能够看到的是NAT所提供的 公网IP:PortA,PeerB将这一点告知PeerA;那么接下来PeerA就可以将这个信息传输出去,其他没有建立连接的PeerC、PeerD知道这个 公网IP:PortA,就能够直接连接到PeerA
对等点(Peer)之间相互通知他们观察到的地址,这个方法就是 STUN (STUN)的基础,它描述了一个用于发现公开可到达的 IP 地址和端口组合的客户机/服务器协议。
Libp2p 的核心协议之一是 Peer Identity ,它允许一个对等点向另一个对等点询问一些标识信息。当发送它们的公钥和其他一些有用的信息时,被标识的对等点包括它为询问问题的对等点观察到的一组地址。
这种外部发现机制的作用与 STUN 相同,但不需要一组“ STUN 服务器”。
Peer Identity 允许一些对等点通过 NAT 进行通信,否则将无法进入。
AutoNAT
尽管上面描述的 Peer Identity 允许对等点相互告知它们所观察到彼此的网络地址,但并非所有网络都允许同一个端口能够 Dialing 的同时能建立接入连接。
在这个情况下,同样还是借助其他节点观察到的情况来认知自身的网络规则。这个过程需求对等点试先尝试拨号我们观察到的地址。如果成功,那我们就能依靠这一点找到一条通信链路,可以开始广播我们的收听地址。
名为 AutoNAT 的 libp2p 协议允许对等点从提供 AutoNAT 服务的对等点请求拨回。AutoNAT 目前通过 go-libp2p-AutoNAT 在 go-libp2p 中实现。
连接问题是说可能端口无法做到出入都是允许的,然后通过测试连接找到一条通畅的连接通路。
使用的工具是: Go-libP2P: This package provides an ambient NAT autodiscovery service. It allows peers to figure out their NAT dialability situation by using test dial backs through peers providing the AutoNAT service.
但是得看看源代码是不是自己建立了一个循环式的进程来获取地址呢?
Circuit Relay (TURN)
有些时候, Peer之间无法提供能够通过公网直接访问的地址。为解决这个情况,Libp2p 提供了一个电路中继协议[[Circuit Relay protocol](https://docs.libp2p.io/concepts/circuit-relay/)],允许对等点通过一个有用的中间对等点间接通信,这与其他系统中的 TURN 协议具有类似的功能。
SECURE COMMUNICATION
暂时无文章
CIRCUIT RELAY
电路中继(Circuit relay)是一种通过第三方 “中继” 来实现 两个对等点之间路由通信的传输协议。
在许多情况下,对等网络无法使用上章所讲的NAT通信策略,也存在防火墙问题(traverse their NAT and/or firewall),从而无法公开访问彼此。或者是网络之间的通信协议并不相同而无法直接建立连接。
为了能够解决NAT等阻碍对等网络链接的障碍,libp2p 定义了一个称为 p2p-circuit 的协议。当一个对等点无法 Listening 公共地址时,它可以 Dialing 一个长期处于连接开放状态的中继对等点。这样其他节点将能够通过这个中继节点拨号使用一个 p2p-circuit 地址,这将转发流量到其目的地。
电路中继协议的灵感来自于 TURN,它是 Interactive Connectivity Establishment 技术的一部分。
Interactive Connectivity Establishment (ICE):A Protocol for Network Address Translator (NAT) Traversal这个文档描述了基于 UDP 通信的网络地址转换器的协议(NAT)传输。这个协议被称为交互式连接建立(ICE)。 ICE 利用STUN (STUN)协议及其使用中继 NAT (TURN)遍历。
中继连接是端到端加密的,这意味着作为中继的对等方无法读取或篡改通过该连接的任何流量。
中继协议的一个重要方面是它不是“透明的”。换句话说,源和目的地都知道网络传输的状态和节点信息。这非常有用,因为目的地可以看到用于打开连接的中继地址,并且可以使用它构造返回源的路径。它也不是匿名的——所有的参与者都使用他们的对等 ID 来识别,包括中继节点。
Protocol Versions
目前有两个版本的电路中继协议:v1和 v2。LibP2P使用后者而不是前者。详情参阅电路继电器 v2规范,了解两者的详细比较。同时,本文档也描述了电路中继 v2协议。
Relay addresses
一个中继电路会使用上文说到的多地址进行标识,这个地址将包括正在中继通信的对等点(监听对等点或“中继目标”)的peer id
举例子来理解这个概念:假设我本地计算机 ID 为 QmAlice,我想把我的地址发给朋友 QmBob,但是我是在一个局域网的NAT后面且这个NAT不允许任何人外部 Dialing 我的计算机。
此时按照上文所说,我先构建最基本的P2P电路地址为:
/p2p-circuit/p2p/QmAlice
这个简略的地址并没有包括我计算机相关的任何传输地址,也不包括将传输流量的中继对等点的任何传输地址;当然这样的话其他计算机是无法与我的计算机通信的;这个地址只是标记了网络当中我计算机的名称。
之后在这个简略的地址上加特定中继对等点 QmRelay 的标识,表现为:
/p2p/QmRelay/p2p-Circuit/p2p/QmAlice
这个改进可以保证一个计算机如果已经知道如何连接中继节点 QmRelay ,那这台计算机就能够找到我。
那如何找到这个中继节点呢?接下来在地址中加入中继对等点的传输地址。假设我本地已经用对等 ID QmRelay 建立了一个到特定中继节点的连接;通过识别协议中继节点告诉我,他正在监听 IPv4地址为7.7.7.7的端口55555上的 TCP 连接,那么我可以构建一个地址,描述一条通过传输上的特定中继线路到达我计算机的路径:
/ip4/7.7.7.7/tcp/55555/p2p/QmRelay/p2p-circuit/p2p/QmAlice
在/p2p-circuit/ 之前的所有内容都是中继对等点的地址,其中包括传输地址和中继节点的Peerid :id QmRelay。在/p2p-circuit/ 之后是另一端的计算机(对等点) QmAlice 的Peer id。
通过将完整的中继路径提供给我的朋友 QmBob,他们能够快速建立中继连接,而不必“四处询问”有 QmAlice 路由的中继。
当广播这个地址时,最好提供包含中继对等点传输地址的中继地址。如果中继有许多传输地址,您可以通过它们中的每一个来宣传一个 p2p 电路。
Process
下面的时序图描述了一个中继过程:
-
节点
A处于 NAT或者(和)防火墙之后,比如通过 AutoNAT service 检测到了。 -
节点
A请求中继节点R并要求R代表它侦听传入的连接。 -
节点
B期望想要建立一个到 节点A的连接. G由于上述所说A处于 NAT 之后没有可以直接访问的地址,仅只是公布了中继地址, 那么 节点B就会先连接到中继节点R, 请求节点R中继连接到 节点A. -
中继节点
R将请求转发给节点A并最终作为中继(桥梁)不断转发节点A与节点B之间传输的数据。
PROTOCOLS
在编写网络应用程序时,处处都需要协议,而 libp2p 中的协议尤其多。 本文所讲的协议是使用LibP2P编写的协议,这些协议都基于使用libp2p 抽象出来的对象,如Transport、PeerIndentity、Addressing等。
在本文中,把使用 libp2p 构建的这种协议称为 libp2p 协议,但是也有部分地方称它们为 “wire protocols” 或 “application protocols” ,这些都是定义应用程序并提供其核心功能的协议。
本文将介绍 libp2p 协议的一些关键定义特性,概述协议协商过程,并概述 libp2p 包含的一些核心 libp2p 协议并提供关键功能。
What is a libp2p protocol?
Libp2p 协议具有以下关键特性:
Protocol Ids
Libp2p 协议具有唯一的字符串标识符,这些字符串标识符用于第一次建立连接时候的 protocol negotiation 过程。
按照惯例,协议的 ids 有一个类似路径的结构,结构最后的内容是版本号:
/my-app/amazing-protocol/1.0.1
如果协议的连接形式或者语义产生了巨大的修改就会带来一个新的版本号,而有关在拨号和监听过程中版本选择如何工作的更多信息,请参见protocol negotiation section。
虽然 libp2p 在技术上接受任何字符串作为有效的协议 ID,但是使用带有版本组件的推荐路径结构既对开发人员友好,又能够更容易地按版本进行匹配。
Handler functions
为了接受连接,libp2p 应用程序将使用 switch (aka “swarm”) 的协议 ID 或更高级别的接口(如 go’s Host interface )为协议注册处理程序函数。
当使用已注册的协议 ID 标记传入流时,将调用处理程序函数。如果使用 match 函数注册处理程序,则可以选择是否接受协议 ID 的非精确字符串匹配,例如,在语义主要版本上进行匹配。
如果传入流是被在注册过协议ID标识时,handler function 就会被触发。如果使用 match function 注册处理程序,就可以选择是否接受协议 ID 的非精确字符串匹配,例如,在 semantic major versions 上进行匹配。
Binary streams
Libp2p 协议使用的 “媒介” 是具有以下属性的双向二进制流:
- 双向且可靠的二进制数据传输
- 每一方都可以在任何时候从流中读写。
- 读取数据的顺序与写入数据的顺序相同。
- 可以是“半封闭式”,即封闭式写作和开放式阅读,或封闭式读作和开放式写作。
- 能够承受较大压力
- 热读取进程不会被大量的写入进程侵占干扰。
除此之外,libp2p 还将确保流的安全性和高效复用性。这对于协议处理程序(Handler)是透明的,它通过流读取和写入未加密的二进制数据。
二进制数据的格式和什么时候发送、由谁发送的机制都由协议来决定。作为抛砖引玉,下面概述了 libp2p 内部协议中使用的一些常见模式。
Protocol Negotiation
当本地 “Dialing” (拨号对象)发起一个新的 流 时,libp2p进程将发送本地想要使用的协议的 Protocol ID;另一端正在侦听的Peer(侦听对象)将根据已注册的协议处理程序检查传入的Protocol ID;
如果侦听对象不支持请求的协议,它将结束流,拨号对象就需要使用不同的协议或最初请求的协议的后备版本再次尝试。
如果支持协议,侦听对象将回传Protocol ID,作为未来通过流发送的数据将使用的协议语义信号。
以上所说给定流或连接使用什么协议达成一致的过程称为协议协商。
Matching protocol ids and versions
当要注册使用 protocol handler 时,可以使用两种方法。
1. Using a match function
使用参数匹配,有着严苛的字符匹配和版本匹配要求,在这个方式下有两种参数匹配的方式:
第一种方式包含有两个参数: 一个Protocol ID 和一个Handler函数。如果传入流请求发送与 Protocol ID 完全匹配的内容,则将使用新流作为参数调用处理程序函数。
第二种协议注册有三个参数: Protocol ID、协议匹配函数和 Handler 函数。
当流请求的 Protocol ID 没有任何精确匹配时,协议 ID 将遍历所有已注册的匹配函数。如果有任何返回 true,则将调用相关联的处理程序函数,这提供了很大的灵活性,可以进行自己的“模糊匹配”,并定义对应用程序有意义的任何协议匹配规则。
灵活性是体现在自定义的参数处理以及匹配方式足够多的时候,总能够在遍历结果当中找到一个?
2. Match using semver
如果您想同时支持一系列编号版本,那么您可能需要使用语义版本控制(又名 semver)。
在 go-libp2p 中,有一个名为 Multistream SemverMatcher 的辅助函数作为协议匹配函数,以查看注册的协议版本是否能够满足传入请求。
Js-libp2p 作为 js-multistream-select 的一部分提供了类似的 similar match function。
Dialing a specific protocol
当 Dialing 一个远程对等点建立一个新的流时,初始需要建立连接的点发送他们想要使用的协议 ID。远程接收对等点将使用上述匹配逻辑来接受或拒绝协议。如果协议被拒绝,则拨号对等方可以再次尝试。
拨号时,可以选择发送想使用的协议 ID 列表而不是单个 id。当提供多个协议 ID 时,将连续尝试每个协议 ID,如果远程对等方至少支持其中一个协议,则将使用第一个成功的匹配。如果拨号方能支持一系列协议版本,这将非常有用,因为拨号对等方可以提出最新版本,如果远程还没有采用最新版本,则可以回退到旧版本。
Core libp2p protocols
除了在开发 libp2p 应用程序时编写的协议之外,libp2p 本身还定义了几个用于核心特性的基本协议。
Common patterns
下面描述的所有协议都使用(协议缓冲)protocol buffers(别名 Protobuf)来定义消息模式
消息通过一个非常简单的约定进行交换,该约定在二进制消息有效负载前加一个整数,该整数表示有效负载的长度(以字节为单位)。长度被编码为一个原型程序 varint (变长整数)。
Protocol Buffers,是Google公司开发的一种数据描述语言,也是一种语言中立,平台无关,可扩展的序列化数据的格式 ,类似于XML、json能够将结构化数据序列化,可用于数据存储、通信协议等方面。 我理解就是一个 依据特征标识存储关键信息,然后生成对应的不同类型代码数据结构的工具包,网络上的编译器?
Ping
| Protocol id | spec | implementations |
|---|---|---|
/ipfs/ping/1.0.0 | N/A | go , js , rust |
Ping 协议是一个简单的在线检查机制,对等点可以使用它来快速查看另一个对等点是否在线。
在初始协议协商之后,拨号对等点发送32字节的随机二进制数据。侦听监听对等点回传数据,拨号对等点将验证响应并测量请求和响应之间的延迟。
Identify
| Protocol id | spec | implementations |
|---|---|---|
/ipfs/id/1.0.0 | identify spec | go , js ,rust |
标识协议允许对等方交换彼此的信息,最明显的是它们的公钥和已知的网络地址。
基本标识协议通过使用上表所示的标识协议 id 向对等方建立一个新的流来工作。
当远程对等方打开新的流时,它们将找到一个标识的 protobuf message ,其中包含有关它们自己的信息,例如它们的公钥,这个公钥用于派生它们的 PeerId。
重要的是,Identity 消息包含一个观察到的 Addr 字段,该字段包含对等方观察到进入请求的多地址。这有助于对等点确定他们的 NAT 状态,因为它允许他们看到其他对等点观察到的公共地址,并将其与他们自己的网络视图进行比较。
identify/push
| Protocol id | spec & implementations |
|---|---|
| /ipfs/id/push/1.0.0 | identify spec |
标识稍有变化,即便没有请求,标识/推送协议也会主动发送相同的标识消息,这个机制有利于一个对等方开始监听一个新地址,建立一个新的中继电路,或者使用标准识别协议从其他对等方那里了解到它的公共地址。与此同时,在创建或学习一个新地址时,对等方可以将新地址推送给它当前知道的所有对等方,这个做法使得每个人的路由表保持最新,并使其他对等机更有可能发现新地址。
secio
|Protocol id|spec|implementations |
|---|---|---|
|/secio/1.0.0|secio spec|go, js |
Secio (安全输入/输出的缩写)是一种类似于 TLS 1.2的加密通信协议。
塞西奥现在已经被否定了,我们建议不要使用它。它已经被 TLS 1.3和噪声作为首选的安全传输方式所取代。有关详细信息,请参阅这篇博客文章。
We're removing support for the SECIO security transport 这篇文章讲述了这件事情。 替换之后的对象是 noise 作为安全传输方式
kad-dht
| Protocol id | spec | implementations | |
|---|---|---|---|
/ipfs/kad/1.0.0 | kad-dht spec Kad-dht | go , js | rust |
Kad-dht 是一个基于 Kademlia 路由算法的分散式哈希表,经过了一些修改。Libp2p 使用 DHT 作为其对等路由和内容路由功能的基础。
Kad以全局唯一id标记对等网络节点,以节点ID异或(XOR)值度量节点之间距离,并通过距离分割子树构建路由表,建立了一种全新的网络拓扑结构
Circuit Relay
| Protocol id | spec | implementations |
|---|---|---|
/libp2p/circuit/relay/0.1.0 | circuit relay spec | go , js |
如Circuit Relay article所述,libp2p 提供了一个协议,用于在两个中继节点无法直接连接时通过中继节点隧道传输流量。
PEER IDENTITY
一个 Peer Identity(通常编写为 PeerId)是对整个对等网络中特定对等点的唯一标识(引用或者说标记)。
PeerId 不仅可以作为每个对等网站的唯一标识符,还可以作为对等点与其公共单密钥之间的可验证链接。
意思是:二者一一对应
What is a PeerId
每个 libp2p 对等点控制一个私钥,这对于所有其他对等点保密,同时每个私钥都有一个对应的公钥,而公钥与其他对等体共享。
公钥和私钥(或“密钥对”)一起允许对等点彼此建立安全的通信通道(secure communication)。 从概念上讲,PeerId 是对等方公钥的加密散列。当对等点建立安全通道时,哈希值可以验证用于保护通道的公钥是否与用于标识对等点的公钥相同。
PeerId spec 详细介绍了用于 libp2p 公钥的字节格式,以及如何散列密钥以生成有效的 PeerId。
PeerID 使用多哈希(multihash)进行编码,这个实质就是在哈希前添加一个小标头,用于标识用于生成该标头的哈希算法。
指的是加密后的密文?还是哈希函数?
How are Peer Ids represented as strings?
PeerID 是 multihashes ,它被定义为一种紧凑的二进制格式。
很常见的情况是,多哈希被编码成58进制,使用比特币使用的相同字母表。
下面是一个表示为 base58编码的多哈希的 PeerId 示例:
QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
虽然可以使用各类文本格式表示多哈希(例如十六进制、 base64等) ,但 PeerID 总是使用 base58编码,同时在将其编码为字符串时不使用multibase prefix。
PeerIds in multiaddrs
PeerId 可以作为/p2p 地址当中的参数编码到multiaddr 中。如果我的对等 ID 是 QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
那么对我来说 libp2p 多地址应该是:
/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
正如前文所讲的multiaddr定义,可以将 /p2p 地址封装到另一个 multiaddr 中,以组成一个新的 multiaddr。
例如,可以将上面的内容与传输地址/ip4/7.7.7.7/tcp/4242结合起来,生成这个复合的多信息地址,可以提供足够的信息,通过 TCP/IP 传输拨号给特定的对等点
/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
PeerIdentity 的安全性是毋庸置疑的,如果某个其他对等点能够接管该 IP 地址或端口,由于它们将无法控制用于生成嵌入在该地址中的 PeerId 的密钥对,就不能够建立连接而产生安全问题。
Libp2p 地址的 multiaddr 协议最初是编写/ipfs 的,后来被重命名为/p2p。两者是等价的,并且在 multiaddr 中具有相同的二进制表示。以字符串格式呈现哪一个取决于所使用的 multiaddr 库的版本。
PeerInfo
另一个与对等标识相关的常见 libp2p 数据结构是 PeerInfo 结构。
PeerInfo 将 PeerId 与对等方正在监听的一组多址组合在一起。
Libp2p 应用程序通常会保留一个“对等存储”或“对等图书”,用于为它们所知道的所有对等点维护一个 PeerInfo 对象集合。
对等存储在拨号给其他对等存储时扮演着一种“电话簿”的角色; 如果一个对等存储在对等存储中,就不需要使用对等路由来发现他们的地址。
CONTENT ROUTING
无
PEER ROUTING
无
ADDRESSING
灵活的网络需要灵活的寻址系统。由于 libp2p 被设计用于跨多种网络工作,因此是需要一种方法来用一致的方式处理许多不同的寻址方案。
多地址(通常缩写为 multiaddr)是将多层寻址信息编码为单个“等待验证”路径结构的规定。它定义了通用传输协议和多层不同种类协议能够使得人类可读和机器优化的编码方式,并允许将多层寻址结合在一起使。
例如:/ip4/127.0.0.1/udp/1234 包含了两个协议及其基本寻址信息。Ip4/127.0.0.1告诉我们需要 IPv4协议的127.0.0.1环回地址,而/UDP/1234告诉我们需要将 UDP 数据包发送到端口1234。
这样子能够让一个地址表示尽可能详细地表示出通讯需要的多种信息,例如,一个多地址/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N 使用 libp2p 的registered protocol id /p2p/和 本地 IPFS 节点公钥的 multihash 唯一标识本地 IPFS 节点。
现在可以举一个实际例子来理解上述所有的过程:
假设我有上面的对等 ID QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N,并且我的公共 ip 是7.7.7.7。我启动我的 libp2p 应用程序并侦听 TCP 端口4242上的连接。
现在我可以开始向我周边所有地邻居广播我的多地址,格式是/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmmE7DWjhx5N。这个地址包含了我的“ location multiaddr”(我的 IP 和端口)与我的“ Identity multiaddr”(我的 libp2p PeerId)。
现在只要知道这个地址就能够找到我,还知道(通过/p2p/protocol id)我可能支持常见的 libp2p 交互,比如打开连接和协商我们可以使用哪些应用程序协议进行通信。
这里说到anyone they give that address to can verify that the machine on the other side is really me, or at least, that they control the private key for my
PeerId指的是只要是这个地址,至少是能找到一个有我私钥的机器?
这可以扩展到更多的寻址和网络抽象内容上。例如,用于电路中继的地址将传输地址与多个对等身份相结合,形成描述“中继电路”的地址:
/ip4/7.7.7.7/tcp/4242/p2p/QmRelay/p2p-circuit/p2p/QmRelayedPeer
SECURITY CONSIDERATIONS
Libp2p 使得在两个对等点之间建立加密的、经过身份验证的通信通道变得简单,但是在构建健壮的对等点系统时,还需要考虑其他重要的安全问题。
整篇文章当中描述的许多问题都没有已知的“完美解决方案”,而且目前存在的解决方案和缓解策略可能会在其他方面有着折衷和妥协。作为一个通用框架,libp2p 试图为应用程序开发人员提供解决这些问题的工具,而不是采用任意的安全方法,这种方法可能不被所有使用 libp2p 构建的系统所接受。
另一个需要考虑的方面是,一种特定类型的攻击在理论上是可行的,但这并不意味着它是实际的、合理的、有价值的或有效率的。为了评估理论攻击向量的实际可利用性,需要考虑攻击者为了获得合理的攻击成功而必须消耗的资源的数量、类别和成本。
Identity and Trust
每个 libp2p 对等点都由其来自私有单密钥生成的唯一 Peer id。对等节点 ID 及其相应的密钥允许我们对远程对等节点进行身份验证,这样我们就可以确保与我们对话的是正确的对等节点,而不是冒名顶替者。 然而,当涉及到安全性时,身份验证通常只是“ auth”故事的一半。许多系统还需要授权,或者能够确定“谁被允许做什么”
Libp2p 没有提供“开箱即用”的授权框架,因为对等系统的需求差异很大。例如,有些网络可能根本不需要授权,只需接受任何对等点的请求,而其他网络可能需要根据角色层次结构、请求的资源或服务等显式地授予细粒度权限。
为了在 libp2p 上设计一个授权系统,你可以依靠对等 ID 的认证,在对等 ID 和权限之间建立一个关联,对等 ID 在传统的授权框架中作为“用户名”,对等的私钥作为“密码”。然后,您的协议处理程序可以拒绝来自不受信任的对等方的请求。
当然,也可以在 libp2p 上构建其他类型的不基于对等 ID 的授权系统。例如,用户可能希望一个 libp2p 对等点能够被许多人工操作员使用,每个操作员都有一个传统的用户名和密码。这可以通过定义接受用户名和密码的,此时如果凭据有效,就返回已签名的令牌进行响应,这样一个协议来实现。当然,如果是涉及公开敏感资源的协议,可能在允许访问之前还需要Token验证。
完全分布式的系统通常是“默认开放的”,这样就可以允许任何节点能够使用架构的核心功能。那也就是说,公开的系统可以相信某种权威机构或者对象的判别,来定义错误或恶意的参与者,同时阻止或忽略他们。例如,每个节点可以根据协议的设计,根据其行为的有用程度和“正确”程度,为每个节点分配分数,在决定是否处理给定请求时将分数考虑在内。
一个完全分布式基于信用的管理系统,其中对等点通过协作来评估彼此,这超出了 libp2p 的范围。但是,许多 libp2p 的核心开发人员和社区成员都对该领域的研究和开发感到兴奋,并欢迎在 libp2p 论坛上发表看法。[招兵买马用,意思就是还没成熟机制]
Cooperative Systems with Abuse Potential
Libp2p 的一些最有用的内置协议是协作的,它们利用网络中的其他对等机来执行有益于每个人的任务。例如,存储在 Kad-DHT 上的数据在“最接近”数据关联键的对等点集之间进行复制,无论这些对等点是否对数据感兴趣。
合作系统天生容易收到某些极端数值的干扰,尽管官方正在研究限制这种攻击影响的方法,但在就目前的 libp2p 来说足够用了。
1. Kad-DHT
Kad-dHT 协议是一个为所有参与者提供共享密钥/值存储系统的分散式哈希表。除了键/值查找之外,DHT 是 libp2p 对等路由和内容路由接口的默认实现,因此在发现网络上的其他对等点和服务方面发挥着重要作用。
Sybil Attacks
DHT 和 p2p 系统通常易受一类名为 Sybil attacks 的攻击,其中攻击者会控制大量具有不同身份(通常称为“ Sybils”)的 DHT 对等点,以充斥网络并获得获得权威的位置。
DHT 查询在完成之前可能需要通过几个对等点进行路由,每个对等点都有机会通过返回不正确的数据或根本不返回数据来修改查询响应。通过控制大量 Sybil 节点(与网络大小成比例) ,攻击者增加了查询查找路径的可能性。为了定位一个特定的密钥,他们可以通过根据 DHT 的距离度量生成与目标密钥“接近”的 ID 来进一步提高他们进入查找路径的机会。
应用程序可以通过对存储在 DHT 中的值进行签名,或者使用内容寻址(其中存储值的加密散列被用作密钥)来防止数据修改,例如在 IPFS 中。但这些策略只是能查看是否被篡改,不能从一开始就防止篡改的发生,也不能防止恶意节点假装数据不存在并完全忽略它。
与 Sybil 攻击非常相似,Eclipse 攻击也使用大量受控节点,但目标略有不同。Eclipse 攻击的目标不是在传输过程中修改数据,而是针对特定的对等方,目的是扭曲他们对网络的 “视图”,通常是为了阻止他们接触到任何合法的对等方(从而“遮蔽”真正的网络)。这种攻击需要大量资源来执行,需要大量的恶意节点才能完全有效。
Eclipse 和 Sybil 攻击很难防御,因为攻击者可以生成无限数量的有效对等 ID。许多针对 Sybil 攻击的实际缓解措施都依赖于某种方式使 ID 生成“昂贵”,例如,通过要求具有现实世界相关成本的工作证明,或者通过从中央受信任的权威机构“创建”和签名 ID。这些缓解超出了 libp2p 的范围,但是可以在应用程序层采用,以使 Sybil 攻击更加困难和/或昂贵得令人望而却步。
受 S/Kademlia 论文的启发,官方目前正计划实施一种并行查询多个不相交查找路径(不共享任何通用中间对等点的路径)的策略。这将大大增加发现“诚实”节点的机会,即使有些节点返回不诚实的路由信息。
2. Publish / Subscribe
Libp2p 的发布/订阅协议允许对等点向给定“主题”内的其他对等点广播消息
默认情况下,gossipsub 接口将使用作者的私钥为所有消息签名,并且在接受或进一步传播消息之前需要有效的签名。这可以防止邮件在传输过程中被更改,并允许收件人对发件人进行身份验证。 但是,作为一个协作协议,对等点可能会干扰消息路由算法,从而破坏通过网络的消息流。
官方正在积极研究如何减轻恶意节点对“八卦子”路由算法的影响,特别是防止 Sybil 攻击。他们预计这可以带来一个更加健壮和抗攻击的 pubsub 协议,但它不太可能防止所有类型的可能的攻击。
PUBLISH/SUBSCRIBE
发布/订阅是一个对等点围绕他们感兴趣的主题聚集的系统。对某一主题感兴趣的 Peer 被认为是订阅了该主题:
Peer 可以向主题发送消息。每条消息都会发送给所有订阅了该主题的Peer:
发布/订阅 的使用示例:
-
Chat Room: 每个房间都是一个发布/订阅主题,客户端发布聊天消息,房间中的所有其他客户端都会收到这些消息。
-
File Sharing: 每个 pub/sub 主题包含一个可以下载的文件。上传者和下载者在 pub/sub 主题中广播他们拥有这个文件的哪些部分,并协调可能在 pub/sub 系统之外发生的下载。
Design goals
在 peer-to-peer 发布/订阅系统中,所有对等点都参与在整个网络中传递消息。对于 P2P 发布/订阅系统,有几种不同的设计,它们提供不同的权衡。理想的属性特征包括:
- 可靠性: 所有消息都会发送到订阅该主题的所有对等方。
- 速度快: 消息传递迅速。
- 效率高: 网络中不会充斥过多的消息副本。
- 弹性强: 同伴可以在不中断网络的情况下加入和离开网络。没有中心故障点。
- 规模大: 主题可以拥有大量的订阅者并处理大量的消息吞吐量。
- 简单性: 系统易于理解和实现。每个对等方只需要记住少量的状态。
Libp2p 目前使用一种名为 gossipsub 的设计。它的名字来源于同伴之间相互八卦他们看到了哪些消息,并使用这些信息来维护消息传递网络。
Discovery
在 Peer 可以订阅主题之前,它必须找到其他 Peer 并与它们建立网络连接。发布/订阅系统本身没有任何发现其他 Peer 的方法。相反,它依赖于应用程序代表它寻找新的Peer,这个过程称为环境 Peer 发现(ambient peer discovery)。
发现 Peer 的备选方法包括:
- 分布式哈希表
- 本地网络广播
- 与现有对等点交换对等点列表
- 集中追踪器或集合点
- 最先建立的对等点列表
例如,在 BitTorrent 应用程序中,上述大部分方法都已经用于下载文件的过程中。通过重用 BitTorrent 应用程序正常运行时发现的对等点,应用程序也可以建立一个健壮的发布/订阅网络。
发现的 Peer 被询问是否支持发布/订阅协议,如果支持,则添加到发布/订阅网络。
Types of peering
在 gossipsub 中,Peer 通过全消息对等点或仅通过元数据对等点相互连接。整个网络结构由以下两个网络组成:
1. Full-message[全连接,全消息,哪都通]
全消息对等用于在整个网络中传输消息的全部内容。这个网络是稀疏连接的,每个 Peer 只连接到其他几个 Peer 。(在 gossipsub 规范中,这种稀疏连接的网络称为网格,其中的节点称为网格成员。)
限制全消息 Peer 的数量是有用的,因为这样可以控制网络流量; 每个 Peer 只将消息转发给少数几个其他Peer,而不是所有Peer。每个Peer都有一个它想要连接到的 目标Peer数目。在这个例子中,每个Peer都希望能够连接到另外3个 Peer,但是实际上只能连接到2-4个Peer:
对等度(也称为网络度 或 D)控制着网络速度、可靠性、弹性和效率之间的权衡。更高的对等级别有助于更快地传递消息,有更好的机会到达所有订阅者,并且更少的机会有任何对等端通过离开而扰乱网络。然而,高对等度还会导致在整个网络中发送每条消息的额外冗余副本,从而增加参与网络所需的带宽。
在 libp2p 的默认实现中,理想的网络对等度为6,4-12之间的任何地方都可以接受。
2. Metadata-only[半连通]
除了稀疏连接的 全消息Peer 网络之外,还有一个密集连接的仅元数据的Peer 网络。这个网络由非全连接P eer 之间的所有网络连接组成。 只有元数据的网络共享有关哪些是可用的信息,并执行功能来帮助维护全消息对等网络。
Grafting and pruning
Peering(P2P网络连接)是双向的,这意味着对于任意两个连接的Peer都认为它们的连接是全消息的,或者两个对等点都认为它们的连接仅元数据的。
任何一个 Peer 都可以通过通知另一个Peer来更改连接类型。Grafting 是将仅元数据Peering 转换为全消息的过程。Pruning是相反的过程; 将全消息Peering 转换为 仅元数据:
当一个 Peer 的全消息Peer太少时,它会随机地将一些只有元数据的Peer Grafting成全消息Peer。
相反,当一个 Peer 有太多的全消息查看时,它会随机地将其中一些删除回仅元数据。
在 libp2p 的实现中,每个 Peer 每1秒执行一系列检查。这些检查叫做心跳。Grafting 和 Pruning发生在这段时间
Subscribing and unsubscribing
Peer 跟踪其直接连接的Peer订阅了哪些主题。使用这些信息,每个Peer 都能够建立一个关于他们周围的主题的画像,以及哪些 Peer 订阅了相对应的主题:
通过发送订阅和取消订阅消息来跟踪订阅情况。当两个Peer之间建立新的连接时,它们首先互相发送订阅的主题列表:
然后,随着时间的推移,每当一个Peer订阅或取消订阅某个主题时,它都会向其周围的每个Peer发送一条订阅或取消订阅消息。这些消息被发送到所有连接的Peer,而不管接收Peer是否订阅了所涉及的主题:
订阅和取消订阅消息与移植和删除消息密切相关。当一个 Peer 订阅一个话题时,它会挑选一些将成为该话题的全消息Peer,并在发送订阅消息的同时向它们发送移植消息:
当一个Peer点从一个主题退订时,它会通知它的全消息Peer,它们的连接在发送退订消息的同时被删除了:
Sending messages
当一个 Peer 想要发布一条消息时,它会将一个副本发送给它所连接到的所有全消息Peer:
类似地,当一个Peer从另一个Peer接收到一条新消息时,它存储该消息并将一个副本转发给它所连接到的所有其他全消息Peer:
在 gossipsub specification 中,Peer 也被称为路由器,因为它们在通过网络路由消息时具有这种功能。
Peer 只会记录最新看到的信息列表。这就使得 Peer 仅在第一次看到消息时才对该消息采取行动,而忽略以前的消息重新传输。
Peer 还可以选择验证接收到的每条消息的内容。什么算作有效和无效取决于应用程序。例如,聊天应用程序可能强制要求所有消息必须短于100个字符。如果应用程序告诉 libp2p 某个消息无效,那么该消息将被删除,并且不会通过网络进一步复制。
Gossip
Peer 会对他们记录的最新消息做计算处理。每隔1秒钟,每个对等点随机选择6个只有元数据的对等点,并向它们发送一个最近看到的消息列表。
Gossip 可以让Peer注意到他们可能遗漏的网络信息。如果一个Peer 注意到它重复丢失消息,那么它需要将确实拥有消息的节点设置新的全消息Peer 。 下面是一个示例,说明了如何通过只有元数据的对等机制请求特定的消息:
在 gossipsub specification 中,宣布最近看到的消息的gossip 被称为 IHave 消息,对特定消息的请求被称为 IWant 消息。
Fan-out
Peer 可以将消息发布到未订阅的主题。有一些关于确保这些消息可靠地传递的特殊规则。
当一个 Peer 想要将一条消息发布到它没有订阅的主题上时,它会随机选择6个订阅了该主题的 Peer (如下所示3个) ,并将它们作为该主题的Fan-out Peer 记忆起来:
与其他类型的 Peer 不同,Fan-out Peer是单向的; 它们总是从主题外的 Peer 指向订阅该主题的 Peer ,而订阅了该主题的 Peer 不会被告知它们已经被选中,并且仍然将该连接视为任何其他仅元数据的 Peer 。
每次发送者想要发送消息时,它都会将消息发送给 Fan-out Peer,然后Fan-out Peer会在该主题内部分发消息:
如果发送者去发送一条消息,但注意到他们的一些Fan-out Peer 自上次以来消失了,他们将随机选择额外的Fan-out Peer,让此时所选的备选Fan-out Peer数量维持在6.
当一个 Peer 点订阅了一个主题,如果它已经有一些Fan-out Peer,它会希望这些Fan-out Peer成为完整他的全消息Peer:
在两分钟内没有向某个话题发送任何消息之后,所有与该话题相关的Peer都会被清除:
Network packets
Peer 实际上通过网络彼此发送的数据包是本指南中所看到的所有不同消息类型(应用程序消息、拥有/想要、订阅/取消订阅、移植/删除)的组合。这种结构允许在一个网络数据包中批处理和发送多个不同的请求。
下面是整个网络数据包结构的图形表示:
State
下面是每个对等方参与发布/订阅网络时必须记住的状态的总结:
-
Subscriptions: 订阅的主题列表.
-
Fan-out topics: 这些主题是近期接受过消息但没有被(该节点)订阅,接收消息的时间被记录。
-
List of peers currently connected to: 对于连接到的每个Peer ,状态包括它们订阅的所有主题,以及每个主题的Peer是全消息、仅元数据还是分散的。
-
Recently seen messages: 这是最近看到的消息缓存。用于检测和忽略重新传输的消息。对于每条消息,状态包括发送者和序列号,这足以唯一地标识任何消息。对于非常近的消息,保留完整的消息内容,以便可以将其发送给请求该消息的任何Peer。
更多信息:
STREAM MULTIPLEXING
流多路复用(流混用)是通过一个通信链路发送多个数据流的一种方式。
它将多个信号合并成一个统一的信号,这样就可以通过 “电线” 传输,然后再进行分路复用(分路) ,之后再输出,并由不同的应用程序使用。
Multiplexing
多路复用绝不是 libp2p 所独有的。大多数通信网络都会涉及某种形式的多路复用,因为传输媒介一般很少,需要由许多用户共享。
LibP2P 这样做是为了共享一个 TCP 连接,使用唯一的端口号来区分流,比如应用程序(如 ipfs)使用的多个进程(如 kademlia 和 gossipsub)之间的流,以提高连接和传输的效率。使用混合技术,libp2p 应用程序可以在 Peer 之间有许多单独的通信流,也可以在同一时间与 Peer 打开多个并发流。
流多路复用允许用户在与对等方交互的整个生命周期中初始化和使用相同的传输连接。对于混频,只需要处理一次 NAT 遍历就可以打开所需要的任意数量的流,因为它们都将共享相同的底层传输连接。
Libp2p 为流多路复用器提供了一个公共接口,并提供了几个可用的实现。应用程序可以支持多路复用器,如果远程对等机不支持首选项,则可以使用广泛支持的多路复用器
Where it fits in the libp2p stack
Libp2p 的多路复用发生在“应用层”,这意味着它不是由操作系统的网络堆栈提供的。然而,编写 libp2p 应用程序的开发人员很少需要直接与流多路复用器交互,除非在初始配置期间控制启用哪些模块。
Switch / swarm
Libp2p 在称为Switch (swarm,取决于实现) 的组件中维护一些关于已知对等点和现有连接的状态。交换机(Switch)提供了一个拨号和监听接口,该接口抽象出在给定连接中使用哪个流多路复用器的细节。
在配置 libp2p 时,应用程序启用流混合模块,交换机在拨号Peer 和侦听连接时将使用这些模块。如果远程Peer支持任何相同的流混合实现,交换机将在建立连接时选择并使用它。
如果Dialing的交换机已经有一个打开连接的Peer ,新的流将自动在现有连接上进行多路复用。
在连接建立过程的早期,就使用哪个流复用器达成一致。对等方使用协议协商来商定共同支持的多路复用器,该多路复用器将“原始”传输连接升级为能够打开新流的混合连接。
Interface and Implementations
Interface
流多路复用接口定义了如何将流多路复用模块应用于连接,以及多路复用连接支持哪些操作。
Implementations
在 libp2p 中有几个流复用模块可用。请注意,并非每个 libp2p 语言实现都支持所有流混合器。
mplex
Mplex 是为 libp2p 开发的协议。该规范定义了一个简单的多路复用协议,该协议在 libp2p 语言实现中得到广泛支持:
- Go: go-mplex
- Javascript: js-mplex
- Rust: rust-libp2p mplex module
- Rust: ust-libp2p 复用模块
yamux
Yamux 是一个由 Hashicorp 设计的多路复用协议。 Yamux 提供比 mplex 更复杂的流控制,并且可以通过一个连接扩展到数千个多路复用流。 Yamux 目前支持去和生锈:
- Go: go-smux-yamux
- Rust: rust-libp2p yamux module.
- Rust: ust-libp2p yamux 模块。
quic
QUIC 是一个传输协议,它包含一个“本地”流多路复用器。Libp2p 将自动为使用快速传输的流使用本机多路复用器。 Quic 目前通过 go-libp2p-quic-Transport 支持。
spdy
SPDY 是 Google 开发的协议,是 HTTP/2的前身。SPDY 实现了一个流多路复用器,得到了一些 libp2p 实现的支持:
- Go: go-smux-spdystream
- Javascript: js-libp2p-spdy
muxado
Muxado 是一个 Go stream muxing 库,由 go-libp2p via go-smux-muxado 支持。