LocalSend 技术解析:基于 LAN 的无网文件传输实现、防火墙配置与便携模式源码剖析
2026/9/6 19:22:30 网站建设 项目流程

LocalSend 技术解析:基于 LAN 的无网文件传输实现、防火墙配置与便携模式源码剖析

【免费下载链接】localsendAn open-source cross-platform alternative to AirDrop项目地址: https://gitcode.com/GitHub_Trending/lo/localsend

本文以 LocalSend(AirDrop 的开源替代品)的泰语版官方 README 为骨架,结合开源仓库源码,完整讲解这个跨平台应用的核心定位、各平台下载渠道、防火墙与路由器配置要求、便携模式(Portable Mode)与--hidden托盘启动参数的源码级实现原理,以及如何从源码构建该应用。读完之后,你将能够正确配置 LocalSend 的网络环境、理解其设备发现与 HTTPS 自签名证书的底层机制,并独立完成从源码到运行的完整构建流程。

一、LocalSend 是什么

LocalSend 是一款开源跨平台应用,让文件与消息可以在局域网内的设备之间安全传输,全程不需要互联网连接,也不依赖任何第三方服务器。与其他依赖外部服务器的传输/消息类应用不同,LocalSend 基于 REST API 和 HTTPS 加密实现设备间通信,是一款面向本地网络(LAN)的快速、可靠的传输方案。

从仓库结构看,整个项目由三大部分组成:

  • Flutter 应用:位于 app/ 目录,包含 Android、iOS、macOS、Windows、Linux 各平台壳层,业务代码在 app/lib/;
  • Rust 核心库:位于 packages/core/,实现协议、多播发现、HTTPS 服务、加密与 WebRTC 等底层能力;
  • Flutter ↔ Rust 桥接层:位于 packages/localsend_isolates/,通过 Rust 隔离进程(isolates)调用核心能力,并附带 CLI 工具 cli/。

仓库根目录下的 Cargo.toml 定义了多包工作区,pubspec.yaml 与 app/pubspec.yaml 则管理 Dart 侧依赖。

二、下载与分发渠道

由于应用内没有自动更新(auto-update)功能,官方推荐从应用商店或包管理器下载,以便获得持续维护的版本。各平台的分发渠道如下表:

WindowsmacOSLinuxAndroidiOSFire OS
WingetApp StoreFlathubPlay StoreApp StoreAmazon
ScoopHomebrewNixpkgsF-Droid
ChocolateyDMG 安装器SnapAPK
EXE 安装器AUR
Portable ZIP 便携版TAR / DEB / AppImage(均取自最新版 Release)

几个值得注意的渠道细节:

  • Linux 渠道最多:除 Flathub、Snap、AUR 外,还可从 GitHub Release 获取 TAR、DEB、AppImage 原始发行版,其中 TAR/AppImage 正是后面"便携模式"的典型载体;
  • Windows 二进制文件经过代码签名,签名策略详见 CODE_SIGNING.md;
  • 各平台的打包脚本集中在 support/scripts/,例如 compile_windows_exe.ps1、compile_mac_dmg.sh、compile_android_apk.sh 等,维护者可据此了解各发行版的实际构建方式。

三、网络配置:防火墙端口与路由器设置

大多数情况下 LocalSend 可以开箱即用。但如果你遇到"设备互相发现不了"或"发送/接收失败",通常需要在防火墙中放行 LocalSend 的局域网通信。官方给出的防火墙规则如下:

Traffic Type(方向)Protocol(协议)Port(端口)Action(动作)
Incoming(入站)TCP, UDP53317Allow(允许)
Outgoing(出站)TCP, UDPAny(任意)Allow(允许)

同时,请确认路由器上的AP isolation(AP 隔离)已关闭。该设置通常默认关闭,但部分路由器(尤其是访客网络)会默认开启;开启后设备之间的直连通信会被路由器禁止,导致设备不可见。

端口 53317 的源码依据

入站端口 53317 并非随意选择,它在 Rust 核心库中是全局统一的默认端口——HTTP 服务端口与 UDP 多播端口是同一个值:

  • 多播发现模块 packages/core/src/multicast/mod.rs 中定义了pub const DEFAULT_PORT: u16 = 53317;,注释明确写道"与默认 HTTP 服务端口相同";
  • CLI 工具的默认端口同样来自 cli/src/storage/config.rs 中的const DEFAULT_PORT: u16 = 53317;,且 CLI 支持通过config.toml覆盖该值(见 cli/src/main.rs 中--port参数注释 "Port of the HTTP server [default: config.toml, else 53317]");
  • 核心测试 packages/core/src/http/client/url.rs 中的 URL 构造断言也全部基于 53317,例如https://192.168.1.1:53317/api/localsend/v2/register

这也解释了防火墙表格的形态:入站必须精确放行53317 的 TCP + UDP(TCP 承载 HTTPS 传输,UDP 承载多播发现),而出站允许任意端口(设备向对端发起连接时可涉及临时端口)。

设备如何被发现:UDP 多播组

从 packages/core/src/multicast/mod.rs 的源码可以看出,LocalSend 的设备发现机制是"只发送、不应答"的 UDP 多播广播:

  • IPv4 多播组为224.0.0.167。源码注释解释了选这个地址的原因:它位于224.0.0.0/24段内,因为部分 Android 设备只能在该 IP 段内接收 UDP 多播消息
  • IPv6 多播组为ff12::fd3a:e420,是协议 v2.2 之上的 LocalSend 扩展(IPv4 仍是基线,IPv6 并行广播);
  • 设备通过周期性发送MulticastMessageV2广播自身,且采用"突发重发"策略(源码中ANNOUNCE_DELAYS为 100ms / 500ms / 2000ms 三次),因为单条数据报很容易丢失,且刚加入网络的设备可能尚未准备好应答;
  • 真正的"应答"不走 UDP,而是由应用层向广播方发起一次 HTTP register 请求完成确认。

发现结果保存在内存设备存储中,见 packages/core/src/discovery/store.rs:每个DiscoveredDevice以**指纹(fingerprint)**去重,并记录其被确认过的各个通道(DeviceChannel)及状态。这与 support/docs/ 中的 dependency-hierarchy.svg 所描绘的"Flutter 应用层 → isolates 桥接层 → core 核心库"依赖关系一起,构成了完整的架构视图。

四、工作原理:REST API + 即时生成的自签名 TLS 证书

LocalSend 使用一套安全的通信协议:设备之间通过 REST API 通信,所有数据都通过 HTTPS 传输,而 TLS/SSL 证书是在每台设备上即时生成的,从而在无需任何 CA 或中央服务器的情况下保证传输安全。

自签名证书的生成细节

"即时生成"的实现位于 packages/core/src/crypto/cert.rs,generate_self_signed()函数生成的设备身份包含:

  • 一个RSA-2048 密钥对(与 Flutter 应用历史上在 Dart 侧生成的证书保持兼容);
  • 一张自签名 X.509 证书,主题 CN 固定为LocalSend User,不带 SAN——因为对端设备之间仅凭证书指纹相互识别,证书名称不承载任何身份信息;
  • 一个SHA-256 指纹(对证书 DER 编码做哈希,大写十六进制),即界面上看到的设备指纹,fingerprint_from_cert_der()负责计算;
  • 有效期采用 rcgen 默认值(1975 年到 4096 年),即证书实际上不会过期,无需因时间原因轮换。

配对信任的建立则依赖指纹比对:packages/core/src/crypto/cert.rs 中的verify_cert_from_pem()/verify_cert_from_der()会依次校验时间有效性、公钥是否匹配(Public key mismatch单独报错以便单测)、以及签名本身。协议层面的完整规范在独立的 protocol 文档仓库中维护,本仓库的 packages/core/ 是其主要参考实现。

五、配置进阶:便携模式与隐藏启动

便携模式(Portable Mode)

v1.13.0引入。操作方法:

  1. 可执行文件(.exe)所在的同一目录创建一个名为settings.json的文件;
  2. 该文件可以是完全空白的文件;
  3. 应用检测到它之后,就会把全部设置存到这个文件里,而不再写入默认位置(用户数据目录)。

源码印证见 app/lib/util/shared_preferences/shared_preferences_portable.dart:

/// Custom implementation of SharedPreferencesStorePlatform /// that uses a file named settings.json located next to the executable. /// This is used to for portable mode. class SharedPreferencesPortable extends SharedPreferencesFile { SharedPreferencesPortable() : super(filePath: _getSettingsPathFromExecutable(), beautify: true); }

几个实现细节值得注意:

  • buildSettingsPath()会把路径解析为可执行文件所在目录 + '/settings.json'
  • 通过Platform.resolvedExecutable定位可执行文件,但特意做了容错:在某些虚拟磁盘(例如 ImDisk 内存盘)上读取该属性会抛出TypeError,导致应用未启动即崩溃,因此源码捕获异常后回退到当前工作目录(fallbackDirectory);
  • 对应单测 app/test/unit/util/shared_preferences/shared_preferences_portable_test.dart 验证了"settings.json 位于可执行文件旁"的路径拼接逻辑(如/opt/localsend/settings.json)。

作为对照,默认的持久化路径(非便携模式)位于 app/lib/provider/persistence_provider.dart,例如 Windows 上是%APPDATA%\LocalSend\settings.json

以系统托盘图标方式启动(Start Hidden)

v1.15.0更新。如果希望应用启动时不显示主窗口、只驻留系统托盘,使用--hidden命令行参数:

localsend_app.exe --hidden

版本历史注意:在v1.14.0 及更早版本中,应用只有同时满足"设置了autostart标志"且"隐藏设置已开启"两个条件才会静默启动;v1.15.0 起改为直接监听--hidden参数。这一点也被 app/assets/CHANGELOG.md 记录:"now listens to--hiddenparameter instead ofautostart"。

各平台的参数解析在源码中都有对应实现:

  • Windows/通用 Dart 侧:app/lib/util/native/autostart_helper.dart 中定义了const startHiddenFlag = '--hidden';
  • Linux 原生壳:app/linux/my_application.cc 在MyApplication::MyApplication()构造阶段遍历dart_entrypoint_arguments,逐一strcmp比对--hidden,命中后跳过首窗口创建、仅启动托盘。

六、从源码构建(Getting Started)

要从源码编译 LocalSend,按以下步骤操作(泰语版 README 的完整步骤):

  1. 安装 Flutter:可以直接安装,也可以使用 fvm 管理版本(注意以仓库中.fvmrc指定的版本为准);
  2. 克隆LocalSend仓库;
  3. 运行cd app进入应用目录;
  4. 运行flutter pub get下载依赖;
  5. 运行flutter run启动应用。

注意:LocalSend 目前要求较旧的 Flutter 版本(见 .fvmrc),系统安装的 Flutter 与要求版本不一致时可能出现编译问题。为了开发环境一致,LocalSend 使用 fvm 管理项目 Flutter 版本——安装 fvm 后,请一律用fvm flutter代替flutter执行命令。

补充:仓库当前 .fvmrc 的内容为{ "flutter": "3.41.9" }。此外,由于核心库与 isolates 桥接层均为 Rust 编写(见 packages/localsend_isolates/rust/),完整构建时还需安装 Rust 工具链,版本约束见仓库根目录的 rust-toolchain.toml。

构建产物命令(面向维护者,需从app目录执行):

# Android flutter build apk # 传统 APK flutter build appbundle # Google Play 用的 AppBundle # iOS / macOS flutter build ipa flutter build macos # Windows flutter build windows # 传统 EXE flutter pub run msix:create # 本地 MSIX flutter pub run msix:create --store # 商店就绪 MSIX # Linux flutter build linux appimage-builder --recipe AppImageBuilder.yml # AppImage

七、参与贡献

LocalSend 欢迎任何形式的贡献,主要有两种途径:

翻译

官方翻译托管在 Weblate 平台(推荐方式),也支持 Fork 仓库后手动添加翻译。翻译文件位于 app/assets/i18n/ 目录,编辑_missing_translations_<locale>.jsonstrings_<locale>.i18n.json即可新增或更新翻译。当前仓库中已有zh-CNjakoar等 60 余个语言的翻译文件。

注意:以@开头的字段/条目不需要翻译——它们不会在应用中显示,仅是给开发者或译者提供上下文说明。

翻译产出的 Dart 字符串类由本地化工具生成在 app/lib/gen/(如 strings_th.g.dart 即泰语绑定),应用通过 app/lib/util/i18n.dart 统一取用文案。

Bug 修复与功能改进

  • Bug 修复:发现问题后请直接提交 PR,并在描述中清晰说明问题与修复方式;
  • 改进建议:如有改进 LocalSend 的想法,请先创建 issue 讨论其必要性。

更多规范见 CONTRIBUTING.md。

八、小结

回到泰语版 README 的主线:LocalSend 的核心价值在于无互联网、无中心服务器的局域网安全传输;其可靠性建立在三个可验证的工程决策之上——全网统一的 53317 端口(TCP/UDP 双协议放行)、UDP 多播"只广播不应答"的发现协议(224.0.0.167/ff12::fd3a:e420双栈)、以及每台设备即时生成的 RSA-2048 自签名证书与 SHA-256 指纹配对机制。配置层面,只需一张两行的防火墙规则表、一个空的settings.json(便携模式)或一个--hidden参数(托盘启动),即可覆盖绝大多数部署场景。

【免费下载链接】localsendAn open-source cross-platform alternative to AirDrop项目地址: https://gitcode.com/GitHub_Trending/lo/localsend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询