Gnutella:一个超越其诞生世界的协议
摘要
回顾Gnutella点对点协议,它如何进入主流并被广泛采用,以及尽管催生它的世界已经消失,它至今仍然存在。
<p><a href="https://lobste.rs/s/ptvaan/gnutella_protocol_outliving_world">评论</a></p>
查看缓存全文
缓存时间: 2026/05/22 04:25
# RickCarlino.com
来源:https://rickcarlino.com/notes/p2p/gnutella-explanation.html
## 一个比创造它的世界更长寿的协议
LimeWire 搜索结果界面,显示有活跃下载任务进行中
Gnutella 是一种文件共享协议,许多人已将它遗忘。它的故事与一项去中心化技术息息相关,这项技术曾被数百万不关心什么是点对点系统的普通用户采用。用户之所以出现,是因为这个协议大规模地解决了实际问题,而解决方案恰好是去中心化的。没人会假装使用 Gnutella 是为了期待他们的 GnutellaCoinTM 日后升值。他们只是下载 MP3。这个网络迅速火爆,然后平稳发展了近十年,最终进入了一个长期但使用量下降的“长尾”状态。
欢迎阅读我对 Gnutella 极其热情的情书。
尽管它如流星般崛起,并成为 2000 年代文件共享现象背后的驱动力,Gnutella 还是基本被遗忘了。部分原因是它作为一种底层技术,隐藏在 LimeWire 等更显眼的项目之下。另一半原因是,现代平台的“围墙花园”模式让大多数互联网用户甚至不再记得文件系统是什么。
Gnutella 项目最初是一个内部演示,在其企业母公司 AOL 取消该项目后泄露到了公众面前。由于它无服务器的去中心化设计,一旦进入公众视野,就再也无法将其收回。它在十年间爆炸性增长,尽管多年来有人试图阻止它,但它至今仍在运作。原始 Gnutella.exe 的副本仍在 archive.org 上,如果你去挖掘的话。
许多人错误地断言 Gnutella 是个失败品,但这并非对其所发生之事的公正评价。Gnutella 扩展到了主流采用(数百万并发活跃用户),并蓬勃发展了整整十年。它从主流视野中消失的真正原因很简单:它诞生的那个世界已不复存在。
Gnutella 经受住了时间的考验,为一个已不复存在的软件用户解决了问题。它今天仍在,只是以降低的容量继续运转着。
## 导致采用的历史条件
2000 年代初,美国消费者经历了一个奇怪的过渡期。互联网普及率在 2000-2001 年左右的某个时候达到了 50%。互联网正从一种供书呆子使用的复杂工具缓慢转变为日常生活的主流部分。在此期间,音乐文件共享成为一种普遍做法,原因有很多:
- 音乐产业拒绝适应消费者偏好的变化。
- MP3 播放器和固态数据存储变得价格亲民且无处不在。
- 低速拨号上网使得音乐流媒体不可行。
- 管理磁盘空间、目录、备份和下载的文件,对于那个时代的普通计算机用户来说,仍然是可接受且可忍受的活动。
这些条件为持续到 2010 年代初的黄金时代奠定了基础。如果你不信,问任何 35 岁以上的人他们对 LimeWire 的记忆。我当时就在那儿,老兄。那可真是疯狂。
Gnutella 缺乏单点故障,这使其难以被扼杀。基础协议虽然简单,但可以通过规范中内置的可选协议扩展轻松扩展。
## 协议特性
原始的 Gnutella 0.48 连接管理器,显示主机、上传、下载和缓存的对等地址
对大多数人来说,Gnutella 是一种文件传输工具。这种分类忽略了协议的一个基本功能。Gnutella 的核心,只是一个用于 blob(任意数据块)的点对点搜索引擎。
我们本可以把它用作穷人的 DNS 系统,或者用作键值对的全局元数据查找表,或者用于 Unreal Tournament 联赛的比赛匹配服务,但这些从未真正发生。Gnutella 擅长根据搜索查询提供文件下载,这就是历史记住它的原因。大量大量的轻松下载。通常是 MP3。
> 资源 [在 Gnutella 上共享的东西] 可以是任何东西:对其他资源的映射、加密密钥、任何类型的文件、可键控资源的元信息等。 —— Gnutella 0.6 草案规范
过程如下:
- 你打开一个使用 Gnutella 协议的桌面应用,比如 LimeWire、BearShare 或 GTK-Gnutella。
- 客户端连接到互联网上的几个对等节点(我稍后会解释如何找到它们)。
- 你在搜索框中输入类似 `LinkinPark.mp3.exe` 的内容。
- 你的查询通过对等节点在网络中向外传播。
- 结果慢慢从世界各地的随机电脑返回。
- 你检查文件名,猜测哪些结果是假的,比较连接速度,希望它们都不是病毒。
- 一旦你选好一个文件,你的客户端会通过 HTTP 直接从用户的电脑下载它的各个部分。
有时你下载错了东西,却意外发现了新内容。或者恶意软件,你永远无法真正确定。这种“觅食”行为随着推荐引擎的出现而消失了,在这个过程中我们也失去了一点点互联网魔法。
客户端通常提供 3-4 个主要功能,镜像了底层协议的 5 种主要消息类型:
1. **查询管理器:** 查询很慢,且遍布数千个对等节点。
2. **文件管理器:** 你指定要共享的目录或路径,以及下载文件的存放位置。
3. **传输管理器:** 用于处理文件传输的双向恢复、分片和管理的方法。
4. **带外附加功能:** IRC 聊天、留言板、搜索查询监视器以及浏览特定主机。这些东西很多实际上并非协议的一部分,就像你的 AI 搜索侧边栏不是 HTTP 规范的一部分一样。
对我来说有趣的是,Gnutella 设法保持了客户端多样性。尽管有像 LimeWire 这样的市场领导者,但仍然有多种选择,独立开发者也可以从头编写一个客户端。
我今年出于乐趣构建了自己的 Gnutella 客户端,完成后我意识到这种互操作性某种程度上是个奇迹。有很多东西不在规范中。有很多东西从未被记录下来。或者存在于附加规范中。协议自然地演化以增加新特性,而且这是有机发生的。
## HTTP 与八卦协议
手绘的笔记本电脑用户网格,通过点对点链接直接交换文件
想象一下,我们所有人的笔记本电脑上都运行着 HTTP 服务器,并且每当需要传输文件时,我们都可以把 IP 地址给朋友。理论上,每个人机器上的 HTTP 服务器就足以进行文件共享,对吧?
如果你今天尝试在你的个人电脑上运行一个 HTTP 服务器,那么内容很可能无法被公共互联网访问。这是因为 NAT(网络地址转换)、防火墙、住宅 ISP 政策以及各种其他因素使得暴露入站 TCP 端口变得困难。
但在 20 年前,情况并非如此。那时,你可以在本地机器上运行一个小型 HTTP 服务器,并将其暴露在公共 IP 地址上。Gnutella 利用这一点,使得每个参与者在八卦对等节点的网格中都能托管文件。其核心是,通过 LimeWire 下载文件(文件传输部分)类似于通过 curl 或 wget 下载文件。
不过,Gnutella 不仅仅是一个带有图形界面的 HTTP 服务器。同样地,HTTP 也不是一个点对点的文件共享网络。我们假设的场景只是一堆位于不同 IP 地址上的 HTTP 服务器。TCP 端口问题大致解决后,还有另一个问题:大多数 ISP,即使在那时,也不提供稳定的静态 IP 地址。你今天给别人的 IP 地址,明天可能就完全不同了。
此外,即使你的 IP 地址没有变化,人们要如何在一个像 `http://74.6.231.21:4000` 这样的随机 URL 上找到你的文件呢?这个 URL 很可能从未被搜索引擎索引过,而且当你合上笔记本电脑盖时它就会离线。
这些都是 Gnutella 旨在解决的问题。除了启动一个 HTTP 服务器,Gnutella 客户端还会运行一个基于 TCP 的八卦协议。这个协议在一个也运行着 Gnutella 并通过 HTTP 提供共享目录的其他对等节点网格中宣布你的存在。对等节点地址、带宽、延迟和搜索查询等信息通过这个网格流动。还有一些处理防火墙的额外工具,不过这些后来需要扩展以应对现代 NAT 问题。
总之,一个节点处理:
1. 使用本地 HTTP 服务器向需要的人传输文件。
2. 通过使用八卦网格的搜索来查找和宣布可用文件。
3. (有时)绕过防火墙的巧妙技巧。
还有一个情况需要处理:作为一个点对点协议,Gnutella 没有中央入口点或用户注册中心。一旦你进入网格,你就**进入**了,八卦信息开始流动。你会发现新的对等节点、入站搜索查询以及其他网络流量。
但是,如果你从未被邀请且没有前门,你该如何进入网格呢?
答案是引导(bootstrapping)。你需要找到几个起始对等节点。之后,你就成为一个功能完整的网络参与者。你甚至会开始找到更多的对等节点,成千上万个,因为你的计算机会偷听到 PONG 消息(稍后讨论)。但我们要如何找到一组起始对等节点呢?这被称为引导。
## 引导
手绘的 Gnutella 引导图,其中新对等节点通过网络缓存和已知对等节点到达网络
Gnutella 全球网络是参与者 IP 地址的大杂烩。如果你能连接到至少一个已连接到主网络的可靠对等节点,你就会开始看到来自很大一部分用户的网络流量。你在网络上停留的时间越长,通过 PONG 消息找到的对等节点就越多。这些对等节点列表会存储到磁盘,以便你下次重新连接时使用。随着时间的推移,列表中的条目会失效,因为 IP 地址会变化,人们也会离线。在这种情况下,你就继续往下遍历列表,直到找到一个有效的对等节点,让你重新回到派对中。但如果你第一次加入网络,或者在长时间离开后重新连接,这就行不通了。这种情况需要一个称为引导的过程。
有很多方法可以实现这一点,我只会介绍最常见的一种,即 GWebCache 系统。我听说过一些轶事(传说?),说过去的文明在 IRC 聊天室中引导他们的客户端,但据我所知,这些客户端今天并不参与网络,如果它们存在过的话(证明我错了,HN,证明我错了)。许多现代客户端使用一种称为 **Gnutella Web Cache** 的引导机制。GWebCache 服务器形成了一个由独立管理的 web 服务器组成的联邦,这些服务器运行着小型的 web 应用,通常是由志愿者运行的 CGI 或 PHP 脚本,具有一些基本职责:
- 记录自愿提供此信息的 Gnutella 参与者的 IP 地址。
- 记录其他 GWebCache 服务器的 IP 或域名,以便在当前服务器宕机时有备份。
- 提供备选 GWebCache 服务器的列表。
- 提供当前已知的 Gnutella 网络参与者 IP 地址列表。
Gnutella 客户端通常会自动联系缓存服务器,而有些客户端则要求你将 IP 复制粘贴到配置文件或设置菜单中。
连接到这些起始对等节点后,你会开始间接地从网络网格内部收集更多对等节点,缓存的使用变得不那么重要。需要指出的是,GWebCache **不是**网络中的中央瓶颈。这仍然是一个真正的 P2P 网络。外面有很多不相关的 GWebCache 服务器,也有很多不使用 GWebCache 服务器就能引导客户端的方法。没有它们,Gnutella 也能生存,只是没那么方便。
好奇的读者可以将 `?get=1&client=TEST&version=1` 添加到以下 URL 的末尾,以获取一个引导列表。不要过多尝试;你会很快被限速。
``
http://cache.jayl.de/g2/gwc.php
http://gweb.4octets.co.uk/skulls.php
http://midian.jayl.de/g2/bazooka.php
http://p2p.findclan.net/skulls.php
http://skulls.gwc.dyslexicfish.net/skulls.php
``
输出将如下所示:
``
H|106.107.193.27:23459|88579
H|182.233.59.26:23464|88581
U|http://bj.ddns.net/skulls/skulls.php|208999
U|http://scissors.gwc.dyslexicfish.net:3709/|341201
``
以 `H` 开头的条目是对等节点。以 `U` 开头的条目是多余的缓存服务器,你可以存储起来以备后用,因为在 P2P 中冗余很重要,记住吗?
回顾一下,我们知道一个 Gnutella 节点(servent)执行基本任务如下:
1. **引导** – 找到一组初始对等节点进行连接。
2. **在 Web 服务器上托管文件** – 大多数文件传输通过 HTTP 进行。
3. **消息传递(八卦和握手)** – 运行网络的 Gnutella 特定协议消息,将在下一节介绍。
现在让我们逐一查看核心消息类型。
## 核心消息类型
Gnutella 23 字节消息头布局,包含消息 ID、负载类型、TTL、跳数以及负载长度字段
Gnutella 是一种基于 TCP 的协议。当一个对等节点连接到另一个接受入站连接的对等节点时,首先会发生握手。你发送 `GNUTELLA CONNECT/0.4` 或 `GNUTELLA CONNECT/0.6`,对方会返回一个肯定响应,此时连接建立,二进制 Gnutella 消息开始流动。
每个二进制消息都以一个 23 字节的报头开始。该报头包含消息 ID、负载类型、TTL、跳数和负载长度。TTL 是消息剩余的生命周期。跳数是它已经传播的距离。合起来,`TTL + 跳数` 告诉你消息最初预期的范围。
报头之后是实际的核心消息:
代码 目的
**PING** 探测活跃的对等节点。负载类型 0x00
**PONG** 回复 PING,包含 IP 地址、端口和共享统计信息。负载类型 0x01
**QUERY** 搜索请求,由你或附近的对等节点发起。负载类型 0x80
**QUERYHIT** 对 QUERY 的肯定响应,包含文件结果记录和用于下载的连接信息。负载类型 0x81
**PUSH** 针对被防火墙阻挡的上传者的变通方案。它要求文件持有者回连到下载者(想象一个连接到你的 HTTP 服务器)。负载类型 0x40
以上五个消息构成了协议的实际核心。还有一个 `BYE` 消息,但并非严格必需。协议消息支持扩展,即附加到普通消息上的额外数据字段,这样客户端可以添加特性而不破坏整个网络。例如,GTK-Gnutella 支持 TLS、IPv6、UDP 以及其他不属于微小核心协议的特性。
## 扩展协议
Gnutella 协议扩展布局,显示 23 字节报头、核心负载以及可选的 GGEP、HUGE 或 XML 扩展区域
五种消息类型构成了规范的实际核心。你可以只实现这些消息,就能拥有一个可工作的 Gnutella 客户端(大概吧)。但规范已有近 30 年历史,生态系统并未停滞不前。
Gnutella 为实现者留出了足够的空间,让他们将新想法塞进旧的数据包中。GGEP,即 Gnutella 通用扩展协议,为客户端提供了一种通用方式,在普通消息内部放置扩展数据。HUGE,即哈希/URN Gnutella 扩展,为客户端提供了一种通过 SHA 哈希而不是文件名来识别文件的方式。我听说它也支持 XML 扩展负载,但规范中用过去时提及此事,我从未在网络上看到过这样的流量。
相似文章
子代理并非唯一方式
本文质疑多智能体系统中默认的子代理编排模式,主张通过共享消息板进行去中心化协调。它介绍了 Blueprint Bulletins 这一功能,允许智能体在共享板上发布自动过期笔记,以实现无需中央编排器的环境协调。
FidoNet:技术、用途、工具与历史(1993)
FidoNet 的技术概述,这是一种使用调制解调器的存储转发电子邮件广域网,于1984年开发,全球拥有超过20,000个节点。
@EvoMapAI: 介绍 GEP(基因组进化协议)。由 EvoMap 开发的网络协议。代理自我进化背后的核心机制…
EvoMap 推出了 GEP(基因组进化协议),这是一种网络协议,使代理能够将成功策略转化为基因和胶囊以实现自我进化,减少重复探索。
审慎策展:多智能体知识库协议
本文介绍了多智能体知识库的审慎策展协议,解决了智能体无状态性和阿谀奉承等治理缺陷。通过仿真评估,该协议在对抗条件下展现出更强的鲁棒性。
SaySynth: 说话机器的简史
从机械到神经AI系统的说话机器发展史的详细概述,并介绍了作者基于macOS文本转语音框架的SaySynth项目背景。