LocalSend 桌面部署实战:防火墙端口、可移植模式与隐藏启动——基于越南语版 README 的全链路技术解读
LocalSend 是一个开源、跨平台的本地文件与消息分享工具,通过局域网内设备点对点通信,无需 Internet 或任何第三方服务器即可安全传输数据。本文以仓库中的越南语版说明文档 README_VI.md 为主体骨架,完整覆盖其"工作原理、防火墙配置、可移植模式、隐藏启动、源码构建、故障排查"六大板块,并结合 Rust 核心包与 Flutter 应用的真实源码,把文档中每一项配置参数(如端口 53317、--hidden 参数、settings.json 文件)落到具体的实现证据上,帮助读者既会配置,也知其原理。
一、项目定位:为什么 LocalSend 不依赖外部服务器
越南语版 README 的"Giới thiệu"(介绍)部分给出了 LocalSend 的核心定义:
LocalSend 是一个跨平台应用,通过 REST API + HTTPS 加密 实现设备之间的安全通信。与其他依赖外部服务器的消息应用不同,LocalSend 不需要 Internet 连接或第三方服务器,是内网通信的可靠方案。
这一设计直接决定了它的网络拓扑特征:每台设备既是客户端又是服务端。从仓库结构可以印证这一点——packages/core 是一个纯 Rust 实现的通信核心包,内部同时包含 HTTP 客户端与服务端(packages/core/src/http/client、packages/core/src/http/server)、UDP 多播发现(packages/core/src/multicast)与 WebRTC 通道(packages/core/src/webrtc)。所有数据在局域网内以 HTTPS 形式流动,任何设备都不把自己的数据交给外部节点。
二、防火墙与路由器配置:端口 53317 的来龙去脉
文档"安装"(Cài đặt)部分指出:多数情况下 LocalSend 开箱即用,但若收发文件失败,需要配置防火墙允许 LocalSend 在局域网内通信,并给出如下流量规则表:
| 流量类型 | 协议 | 端口 | 动作 |
|---|---|---|---|
| 传入(Incoming) | TCP, UDP | 53317 | 允许(Allow) |
| 传出(Outgoing) | TCP, UDP | 任意(Any) | 允许(Allow) |
为什么恰好是 53317? 这个数字在源码中多处硬编码,且承担双重职责——它既是 HTTP 服务端口,也是 UDP 多播广播的端口:
-
多播发现默认端口定义于 multicast/mod.rs#L38-L39:
/// The default multicast port, identical to the default HTTP server port. pub const DEFAULT_PORT: u16 = 53317; -
Flutter 应用侧的常量位于 constants.dart#L14:
const defaultPort = 53317; -
命令行工具 cli/src/storage/config.rs#L27-L34 中同样以
53317为DEFAULT_PORT,并允许在config.toml中覆盖端口配置。
从源码结构看,53317 上同时承载两种流量:UDP 端口用于设备互相"打招呼"(发现阶段),TCP 端口用于建立 HTTPS 数据传输通道(传输阶段)。因此防火墙规则中"传入 TCP+UDP 53317 允许"必须同时放行两种协议,只放行 TCP 会导致设备无法被邻居发现。
文档还特别提醒:请关闭路由器上的 AP 隔离(AP-Isolation)。该功能默认通常是关闭的,但部分路由器(尤其是访客网络场景)可能已启用。AP 隔离一旦打开,同一 WiFi 下的设备之间将无法直接通信,LocalSend 的多播广播自然也就到不了对方——这与后文"故障排查"表中"设备不显示"的首要原因完全对应。
发现机制补充:设备之间如何互相找到
文档"它如何工作"(Nó hoạt động như thế nào)部分说明 LocalSend 使用安全的通信协议,所有数据经 HTTPS 传输,且 TLS/SSL 证书在每个设备上即时生成,确保安全性。证书细节见下文第五节;这里补充设备发现的细节,来自 multicast/mod.rs#L24-L49:
/// The multicast group used by LocalSend.
/// It is inside `224.0.0.0/24` because on some Android devices this is the only
/// IP range that can receive UDP multicast messages.
pub const DEFAULT_MULTICAST_GROUP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
/// The IPv6 multicast group used by LocalSend, a transient (`ff1x::`) group
/// with link-local scope.
pub const DEFAULT_MULTICAST_GROUP_V6: Ipv6Addr =
Ipv6Addr::new(0xff12, 0, 0, 0, 0, 0, 0xfd3a, 0xe420);
两个值得注意的实现细节:
- IPv4 组选在
224.0.0.0/24网段(224.0.0.167),原因是部分 Android 设备上只有这个网段能收到 UDP 多播包——这是移动端兼容性驱动的取舍; - IPv6 发现是 LocalSend 在协议 v2.2 之上的扩展,采用 link-local 范围的瞬态组,IPv4 仍是基线,IPv6 并行广播,双栈网络下发现更可靠。
广播还会重复发送以对抗丢包,节奏定义在同文件 ANNOUNCE_DELAYS:间隔 100ms、500ms、2000ms 三次重发。因为"单个数据报很容易丢失,且刚接入网络的设备可能还没准备好响应",所以一个发现周期内会广播多轮,这也是"设备列表出现有延迟"现象的根源。
三、可移植模式(Mobile Mode):settings.json 的作用
文档中"Chế độ di động"(可移植模式,v1.13.0 引入)说明:
- 在与可执行文件同一目录下创建一个名为
settings.json的文件,文件可以为空; - 应用检测到它后,就会改用该文件保存设置,而不是使用系统默认位置。
其实现逻辑在 persistence_provider.dart#L110-L129:
final portableStore = SharedPreferencesPortable();
bool usingLegacyStore = false;
if (checkPlatform(const [TargetPlatform.windows, TargetPlatform.linux, TargetPlatform.macOS]) && portableStore.exists()) {
_logger.info('Using portable settings.');
SharedPreferencesStorePlatform.instance = portableStore;
}
即:仅在 Windows / Linux / macOS 三个桌面平台上检查,且文件必须已存在(空文件即可)才会切换存储后端,移动端不会触发该逻辑。settings.json 的具体路径解析在 shared_preferences_portable.dart#L18-L23:取 Platform.resolvedExecutable 所在目录拼接 settings.json;若 VM 无法解析可执行文件路径(例如某些 ImDisk RAM 虚拟盘场景会直接抛 TypeError,见文件内引用的 issue #3021 注释),则回退到当前工作目录。
这个模式的价值在于:把全部用户配置(常用设备、历史、安全上下文等持久化数据)收敛到可执行文件旁边,U 盘拷贝整个目录即可带着配置走。调试页面 debug_page.dart#L22 还会显示当前是否处于可移植模式,方便用户验证配置是否生效。
四、隐藏启动:--hidden 参数与各平台的自启动实现
文档"Bắt đầu ẩn"(隐藏启动,v1.15.0 更新)部分说明:
- 使用
--hidden标志即可让应用隐藏启动(只驻留系统托盘),例如localsend_app.exe --hidden; - v1.14.0 及更早版本中,只有当
autostart标志被设置且"隐藏"设置开启时才会隐藏启动。
v1.15.0 的改动本质上是把"隐藏"从"自启动配置的副作用"提升为独立命令行参数。参数常量定义在 autostart_helper.dart#L9:
const startHiddenFlag = '--hidden';
该文件同时实现了三大平台的自启动注册/注销(enableAutoStart / disableAutoStart / isAutoStartEnabled),开启自启动时可顺带把 --hidden 写进启动命令:
| 平台 | 注册方式 |
|---|---|
| Linux | 写入 ~/.config/autostart/<包名>.desktop 的 Exec= 行(autostart_helper.dart#L17-L33) |
| macOS | 通过 macOS channel 设置"登录时启动"及最小化启动(autostart_helper.dart#L34-L37) |
| Windows | 写入注册表 HKCU\Software\Microsoft\Windows\CurrentVersion\Run 的 LocalSend 值(autostart_helper.dart#L110-L118) |
而 --hidden 参数本身的消费端在 Linux 桌面入口 my_application.cc#L27-L31,启动时遍历 Dart 入口参数逐一对比 --hidden。这也解释了文档中版本演进的语义变化:旧版本"隐藏"依赖自启动上下文推断,新版本只要进程参数里带着 --hidden 就一律进入托盘驻留,行为更可预测。
五、安全机制:每台设备即时生成的自签名 TLS 证书
文档声称"TLS/SSL 证书在每个设备上快速生成,确保最高安全性"。实现位于 packages/core/src/crypto/cert.rs#L32-L56,函数 generate_self_signed() 的源码注释把这些"快速生成"的细节交代得很清楚:
- RSA-2048 密钥对,与 Flutter 应用历史版本在 Dart 侧生成的证书保持兼容;
- 证书主题名固定为
CN=LocalSend User、无 SAN——因为 对等设备之间仅凭证书的 SHA-256 指纹识别彼此,主题名不承载任何身份信息; - 有效期沿用 rcgen 默认值(1975 年到 4096 年),证书实际上永不过期,因此无需定期轮换。
pub struct SelfSignedCert {
/// The private key, PEM-encoded (PKCS#8).
pub private_key_pem: String,
/// The public key, PEM-encoded (SPKI).
pub public_key_pem: String,
/// The self-signed certificate, PEM-encoded.
pub certificate_pem: String,
/// The SHA-256 fingerprint of the certificate in DER format,
/// encoded as uppercase hex.
pub fingerprint: String,
}
由于设备 ID 就是证书指纹,LocalSend 的配对信任模型(谁与谁交换过文件、指纹是否变化)完全建立在本地生成的密钥材料之上,任何密钥都不经手网络,这正是"无服务器"架构能保持端到端信任的关键。仓库测试 packages/core/tests/v2_tls_pinning.rs 专门验证了 v2 协议下的证书固定(pinning)行为。
六、平台兼容性与下载渠道
文档"兼容性"(Khả năng tương thích)表格给出了各平台最低版本要求,整理如下:
| 平台 | 最低版本 | 备注 |
|---|---|---|
| Android | 5.0 | - |
| iOS | 12.0 | - |
| macOS | 11 Big Sur | 旧机型可通过 OpenCore Legacy Patcher 2.0.2 使用 |
| Windows | 10 | 最后支持 Windows 7 的版本是 v1.15.4,未来可能有更新版本的 backport |
| Linux | N.A. | 依赖:GNOME 需 xdg-desktop-portal 与 xdg-desktop-portal-gtk;KDE 需 xdg-desktop-portal 与 xdg-desktop-portal-kde |
下载渠道方面,文档建议优先从应用商店或包管理器获取,因为应用本身没有自动更新能力。各平台渠道包括:Windows 的包管理器(winget/Scoop/Chocolatey)与 EXE/ZIP 安装包,macOS 的 App Store/Homebrew/DMG,Linux 的 Flathub/Nixpkgs/Snap/AUR/TAR/DEB/AppImage,Android 的 Play Store/F-Droid/APK,iOS 的 App Store,以及 Fire OS 的 Amazon 商店。仓库内 CONTRIBUTING.md 的 distribution 章节对分发渠道有更完整的说明。
七、从源码构建:Flutter 版本必须锁定 3.41.9
文档"Bắt đầu"(开始)部分给出从源码编译的五个步骤,并附有一条重要警告:LocalSend 当前要求较旧的 Flutter 版本,若全局安装的 Flutter 版本与项目要求不匹配,构建可能出错。该要求的权威来源是仓库根目录的 .fvmrc:
{
"flutter": "3.41.9"
}
完整构建步骤(在克隆本仓库后执行):
# 1. 克隆仓库
git clone https://gitcode.com/GitHub_Trending/lo/localsend.git
cd localsend
# 2. 安装 fvm 并锁定项目 Flutter 版本(推荐,替代全局 flutter)
fvm install # 根据 .fvmrc 安装 3.41.9
# 3. 进入应用目录
cd app
# 4. 拉取依赖
fvm flutter pub get
# 5. 启动应用
fvm flutter run
文档原文明确建议:安装 fvm 后,用 fvm flutter 替代 flutter 以获得一致的版本环境("After installing fvm, run fvm flutter instead of flutter")。若坚持使用全局 Flutter,则需自行将其升级到 3.41.9,否则可能遇到与新版本不兼容的构建错误。
八、贡献翻译:i18n 目录结构
文档"Dịch thuật"(翻译)部分推荐通过 Weblate 平台管理翻译,也允许直接 fork 仓库手动贡献翻译。翻译源文件位于 app/assets/i18n 目录,贡献者需要编辑两类文件:
_missing_translations_<locale>.json:记录该语言尚缺失的翻译条目,补全后即可提升覆盖率;strings_<locale>.i18n.json(在仓库中实际落盘为 app/assets/i18n 下的*.json,如 zh-CN.json、vi.json 等):完整的本地化字符串。
当前仓库已包含 50+ 个语言文件(从阿拉伯语到泰语、越南语、中文等),生成后的 Dart 绑定位于 app/lib/gen(strings_*.g.dart 系列)。文档还特别提醒一个易错点:
以
@开头的条目不需要翻译——它们不会以任何形式出现在应用中,只是为翻译者提供文件或上下文说明的元信息。
这一约定可以从 _unused_translations.json 与各 _missing_translations_*.json 文件的命名约定得到印证。
九、故障排查表:五类常见问题与对策
文档"Khắc phục sự cố"(故障排查)部分以表格形式给出五类典型问题,完整继承如下:
| 问题 | 平台(发送方) | 平台(接收方) | 解决方案 |
|---|---|---|---|
| 设备不显示 | 任意 | 任意 | 确认已关闭路由器上的 AP 隔离;若开启,设备间连接会被禁止 |
| 设备不显示 | 任意 | Windows | 确认网络配置为"专用网络"(私有);Windows 在"公用网络"下限制更多 |
| 设备不显示 | macOS、iOS | 任意 | 可尝试在系统设置"隐私"中切换"本地网络"权限 |
| 速度过慢 | 任意 | 任意 | 改用 5 GHz 频段;在两台设备上关闭加密 |
| 速度过慢 | 任意 | Android | 已知问题,源于底层文件流插件的缺陷(flutter-cavalry/saf_stream issue #4) |
从实现视角看,这张表与前述机制一一对应:前三个"不显示"问题全部是 UDP 多播广播到不了对端(AP 隔离、防火墙未放行 53317/UDP、系统级本地网络权限拦截);"速度过慢"则指向 TCP/HTTPS 传输阶段,关闭加密会跳过证书校验与部分握手开销。仓库中的集成测试(如 packages/core/tests/multicast.rs、packages/core/tests/internal_server.rs)为发现与传输链路提供了自动化验证,遇到问题时可先在受控局域网内用这些测试路径定位是发现阶段还是传输阶段失效。
十、小结:配置项与源码证据速查
| 文档配置项 | 行为 | 源码证据 |
|---|---|---|
| 防火墙放行 TCP+UDP 53317 | 53317 同时是 HTTP 服务端口与 UDP 多播端口 | packages/core/src/multicast/mod.rs#L38-L39、packages/localsend_isolates/lib/constants.dart#L14 |
| 关闭路由器 AP 隔离 | 多播广播无法跨 AP 隔离边界 | packages/core/src/multicast/mod.rs#L24-L28 |
可执行文件旁放置空 settings.json |
桌面端改用该文件持久化全部设置 | app/lib/provider/persistence_provider.dart#L115-L119、shared_preferences_portable.dart |
--hidden 启动参数 |
隐藏到托盘启动;自启动注册可附带该参数 | app/lib/util/native/autostart_helper.dart#L9、app/linux/my_application.cc#L27-L31 |
| TLS 证书每设备即时生成 | RSA-2048 自签名证书 + SHA-256 指纹识别对端 | packages/core/src/crypto/cert.rs#L32-L56 |
| Flutter 版本锁定 3.41.9 | fvm 管理项目级 Flutter 版本 | .fvmrc |
以上所有结论均直接对应仓库内文档与源码:文档主体来自越南语版 support/readme/README_VI.md(与英文主 README 内容一致),实现细节可在对应 Rust 核心包、Flutter 应用层与 CLI 目录中逐行核对。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00