+
62
-

回答

这需要我们理解 BitTorrent 的核心概念,并利用强大的库来处理底层的复杂性。

首先,我们要明确一点:“Torrent 服务端”并不同于传统的 HTTP/FTP 服务器。

传统的服务器是“客户端-服务器”(C/S)模型,所有下载者都直接从服务器获取数据,服务器的带宽是瓶颈。而 BitTorrent 是“对等网络”(P2P)模型,它的“服务端”主要扮演两个角色:

Tracker (追踪器): 一个协调服务器,它不存储文件本身,只负责记录哪些 Peer (对等端) 拥有哪些文件块 (Piece)。当一个新的 Peer 加入时,它向 Tracker 查询其他 Peer 的地址,然后直接与其他 Peer 连接交换数据。

Initial Seeder (初始做种者): 第一个拥有完整文件的 Peer。它启动后,其他 Peer 就可以从它那里下载文件的第一批数据块。一旦其他 Peer 下载了某些数据块,它们也可以把这些数据块上传给别的 Peer。

因此,您需要实现的是 .torrent 文件的创建一个 Tracker (或者使用公共 Tracker),以及一个持续运行的 Seeder 客户端

我们将使用业界标准库 libtorrent 的 Python 绑定 python-libtorrent 来完成这个任务,因为它是性能最高、功能最全的选择。

核心步骤概览

环境准备: 安装 python-libtorrent。

第1步:创建 .torrent 元数据文件: 针对您要分发的文件,生成一个 .torrent 文件。这个文件包含了 Tracker 的地址和文件的哈希信息。

第2步:运行 Tracker (可选但推荐): 为了私有分发,您需要运行自己的 Tracker。对于简单演示,我们可以暂时跳过自己编写,而是假设一个 Tracker 正在运行。

第3步:运行 Seeder (您的“文件分发服务”): 这是一个 Python 脚本,它加载 .torrent 文件,并作为第一个种子开始向网络提供文件。

第4步:运行 Downloader (客户端): 其他用户使用这个 .torrent 文件,通过您的 Tracker 找到您的 Seeder 和其他 Peer,进行 P2P 下载。

环境准备

首先,安装 python-libtorrent 库。

pip install python-libtorrent

详细实现步骤

第1步:创建 .torrent 文件

这个脚本会读取一个您指定的文件(例如 my_large_file.zip),并为其创建一个 .torrent 文件。

create_torrent.py

import libtorrent as lt
import time
import os

# --- 配置 ---
# 替换为您的 Tracker 的地址。
# 如果您在本地运行 Tracker,通常是 http://127.0.0.1:6969/announce
# 您也可以使用公共的 open trackers,但不推荐用于私有文件。
TRACKER_URL = "http://127.0.0.1:6969/announce" 

# 您想要分发的文件或目录的路径
FILE_PATH = "./my_large_file.zip" 

# 生成的 .torrent 文件的保存路径
TORRENT_FILE_PATH = "./my_file.torrent" 

def create_torrent_file(file_path, tracker_url, output_path):
    """
    为指定的文件或目录创建 .torrent 文件。
    """
    fs = lt.file_storage()
    # 使用 libtorrent 的工具函数来填充文件存储对象
    lt.add_files(fs, file_path)
    if fs.num_files() == 0:
        print(f"错误: 路径 '{file_path}' 中没有找到文件。")
        return

    # 创建 torrent
    # create_torrent 构造函数需要一个 file_storage 对象
    t = lt.create_torrent(fs)
    t.add_tracker(tracker_url)
    t.set_creator("My Python Torrent Creator")

    # 将其设置为私有 torrent,这样客户端将只连接到此 Tracker 中的 Peer
    # 对于内部文件分发非常有用
    t.set_priv(True)

    print("正在计算文件哈希...")
    # set_piece_hashes 需要一个路径来找到文件并计算哈希
    # 第一个参数是文件/目录的根路径
    root_path = os.path.dirname(os.path.abspath(file_path)) if os.path.isfile(file_path) else os.path.abspath(file_path)
    lt.set_piece_hashes(t, root_path)

    # 获取 torrent 的 B编码 形式
    torrent_data = t.generate()

    # 将 B编码 的数据写入文件
    with open(output_path, "wb") as f:
        f.write(lt.bencode(torrent_data))

    print(f"成功创建 .torrent 文件: {output_path}")
    print(f"Info Hash: {t.info_hash()}")

if __name__ == "__main__":
    # 在运行前,先创建一个用于测试的大文件
    if not os.path.exists(FILE_PATH):
        print(f"正在创建测试文件: {FILE_PATH}")
        with open(FILE_PATH, "wb") as f:
            f.write(os.urandom(20 * 1024 * 1024)) # 创建一个 20MB 的随机文件

    create_torrent_file(FILE_PATH, TRACKER_URL, TORRENT_FILE_PATH)
第2步:运行一个简单的 Tracker

编写一个功能完善、高性能的 Tracker 是一个复杂的任务。幸运的是,有很多开源的 Tracker 可以直接使用。您不需要用 Python 从头写

一个简单的方式是使用 opentracker 或者一个用 Python 编写的简单 Tracker 如 pytracker。

为了演示,您可以下载并运行一个简单的 Python Tracker: bep_0015_tracker.py (这是一个符合 UDP Tracker 协议的简单实现)。

但更简单的方式是假设 Tracker 已经在运行。为了让上面的代码能跑通,您需要一个在 http://127.0.0.1:6969/announce 监听的 Tracker。

第3步:Seeder (文件分发服务)

这个脚本会加载 .torrent 文件,并开始做种。这就是您的“服务端”的核心。它需要一直运行,以便其他 Peer 可以连接并下载。

seed_file.py

import libtorrent as lt
import time
import os

# --- 配置 ---
TORRENT_FILE_PATH = "./my_file.torrent"
# 文件的存放目录
SAVE_PATH = "." 

def run_seeder(torrent_file_path, save_path):
    """
    加载 .torrent 文件并作为 Seeder 持续运行。
    """
    ses = lt.session({'listen_interfaces': '0.0.0.0:6881'})

    params = {
        'save_path': save_path,
        'storage_mode': lt.storage_mode_t.storage_mode_sparse,
    }
    with open(torrent_file_path, 'rb') as f:
        torrent_info = lt.bdecode(f.read())
        params['ti'] = lt.torrent_info(torrent_info)

    h = ses.add_torrent(params)

    print(f"开始为 '{h.name()}' 做种...")
    print(f"Info Hash: {h.info_hash()}")

    try:
        while True:
            s = h.status()
            state_str = [
                '排队中', '检查文件中', '下载元数据',
                '下载中', '完成', '做种中',
                '分配空间', '检查恢复数据'
            ]

            print(f'\r状态: {state_str[s.state]} {s.progress * 100:.2f}% '
                  f'| 下载速度: {s.download_rate / 1000:.1f} kB/s '
                  f'| 上传速度: {s.upload_rate / 1000:.1f} kB/s '
                  f'| 连接数: {s.num_peers} ', end='')

            time.sleep(1)

    except KeyboardInterrupt:
        print("\n停止做种...")
        ses.pause()

if __name__ == "__main__":
    if not os.path.exists(TORRENT_FILE_PATH):
        print(f"错误: .torrent 文件 '{TORRENT_FILE_PATH}' 不存在。")
        print("请先运行 create_torrent.py 来创建它。")
    else:
        run_seeder(TORRENT_FILE_PATH, SAVE_PATH)
第4步:Downloader (客户端)

这个脚本模拟一个用户(Peer)下载文件。它的代码和 Seeder 非常相似,因为在 BitTorrent 网络中,下载者同时也是上传者。

download_file.py

import libtorrent as lt
import time
import sys
import os

# --- 配置 ---
TORRENT_FILE_PATH = "./my_file.torrent"
# 下载的文件将保存到这个目录
SAVE_PATH = "./downloads" 

def run_downloader(torrent_file_path, save_path):
    """
    加载 .torrent 文件并下载内容。
    """
    if not os.path.exists(save_path):
        os.makedirs(save_path)

    ses = lt.session({'listen_interfaces': '0.0.0.0:6882'}) # 使用不同端口

    params = {
        'save_path': save_path,
        'storage_mode': lt.storage_mode_t.storage_mode_sparse,
    }
    with open(torrent_file_path, 'rb') as f:
        torrent_info = lt.bdecode(f.read())
        params['ti'] = lt.torrent_info(torrent_info)

    h = ses.add_torrent(params)

    print(f"开始下载 '{h.name()}'...")
    print(f"保存到: {os.path.abspath(save_path)}")

    try:
        while not h.status().is_seeding:
            s = h.status()
            state_str = [
                '排队中', '检查文件中', '下载元数据',
                '下载中', '完成', '做种中',
                '分配空间', '检查恢复数据'
            ]

            print(f'\r状态: {state_str[s.state]} {s.progress * 100:.2f}% '
                  f'| 下载速度: {s.download_rate / 1000:.1f} kB/s '
                  f'| 上传速度: {s.upload_rate / 1000:.1f} kB/s '
                  f'| 连接数: {s.num_peers} ', end='')
            
            time.sleep(1)
        
        print(f"\n\n下载完成!文件已保存到 '{os.path.abspath(save_path)}' 目录。")
        # 下载完成后,让它再做种 60 秒
        print("现在开始做种 60 秒...")
        time.sleep(60)

    except KeyboardInterrupt:
        print("\n停止下载...")
        ses.pause()


if __name__ == "__main__":
    if not os.path.exists(TORRENT_FILE_PATH):
        print(f"错误: .torrent 文件 '{TORRENT_FILE_PATH}' 不存在。")
    else:
        run_downloader(TORRENT_FILE_PATH, SAVE_PATH)

启动 Seeder (您的分发服务):

打开一个新的终端

运行 seed_file.py,并让它一直保持运行。

python seed_file.py

您会看到它显示状态为“做种中”,等待其他 Peer 连接。

启动 Downloader (客户端):

打开第三个终端

运行 download_file.py。

python download_file.py

您会看到下载进度开始变化,并且 Seeder 终端中的“上传速度”和“连接数”也会相应地变化。下载完成后,它会自动开始做种。

重要注意事项

防火墙和 NAT: P2P 网络最大的障碍是防火墙和网络地址转换 (NAT)。如果您的 Seeder 和 Downloader 位于严格的 NAT 之后,它们可能无法直接连接。libtorrent 支持 UPnP 和 NAT-PMP 来自动进行端口映射,但这不一定总能成功。在生产环境中,确保 Seeder 服务器具有公网 IP 或正确配置了端口转发是至关重要的。

Trackerless (无追踪器) Torrent: libtorrent 强大之处在于它原生支持 DHT (分布式哈希表)、PEX (Peer Exchange) 和 LSD (Local Service Discovery)。这意味着即使没有 Tracker,只要网络中有一个 Peer,新的 Peer 就可以通过 DHT 网络找到它。要在 create_torrent.py 中启用它,只需在创建 torrent 后不调用 t.add_tracker() 即可(或者同时使用 Tracker 和 DHT)。

扩展性: 这个示例非常基础。对于一个真正的分发系统,您需要将 Seeder 脚本作为一个后台服务 (daemon) 来运行,并可能需要一个 Web 前端来上传文件并自动生成和管理 .torrent 文件。

网友回复

我知道答案,我要回答