LocalSend 完整教學
LocalSend 是一個用 Flutter(UI / 平台整合)+ Rust(
core/網路核心) 寫的跨平台 P2P 檔案傳輸 app,主打「只用區網、不需網際網路、不需第三方伺服器」的 AirDrop 替代品。 8.2 萬星、Apache-2.0、官方 protocol 文件公開。適合:分析師 / 開發者桌機 ↔ 手機 / NAS / 同事筆電 跨機搬資料,避開 USB、雲端、IM 訊息平台。 不適合:跨網際網路傳檔(沒有 relay server)、大量人對檔案做版本控管(不是 sync 工具)。
1. 專案定位
| 維度 | 說明 |
|---|---|
| 屬性 | Flutter app(端到端產品)+ Rust core engine(可被其他語言綁定) |
| 規模 | 約 945 個原始檔,跨 app/(Flutter UI)、core/(Rust HTTP/TLS/WebRTC)、common/(Dart 共用 DTO/邏輯)、server/、cli/ 五大模組 |
| Protocol 版本 | 目前 v2.1(app v1.18.0),相容 v1.0、v2.0 |
| 核心 abstraction | 「Device」: 一個 (alias, fingerprint, ip, port, version) 的本機可發現端點,所有 REST API 圍繞它設計 |
| 對齊的同類工具 | AirDrop(Apple)、Quick Share / Nearby Share(Android)、Snapdrop / PairDrop(web-only) |
| 與 Snapdrop / PairDrop 差別 | LocalSend 是 native app(每個平台都打包),有 TLS + 公鑰固定,不只是 WebRTC + browser |
2. 安裝指南(多平台)
LocalSend 強烈建議不要從 GitHub release 直接下載,而是用各平台原生套件管理器(這樣才能拿到更新)。
| 平台 | 建議來源 | 備選 |
|---|---|---|
| Windows | winget install LocalSend.LocalSend | Scoop / Chocolatey / EXE installer / Portable ZIP |
| macOS | App Store(id1661733229) | Homebrew (brew install --cask localsend) / DMG |
| Linux | Flathub (org.localsend.localsend_app) | Snap / AUR / DEB / AppImage / TAR |
| Android | Play Store / F-Droid | APK 直裝 |
| iOS | App Store | (無 sideload 路徑) |
| Fire OS | Amazon Appstore | — |
最低版本需求:Android 5.0 / iOS 12 / macOS 11 Big Sur / Windows 10。
防火牆設定(最常見的「對方看不到我」原因):
| Traffic | Protocol | Port | Action |
|---|---|---|---|
| Incoming | TCP + UDP | 53317 | Allow |
| Outgoing | TCP + UDP | Any | Allow |
另外,Wi-Fi router 的 AP isolation 必須關閉(很多 guest network 預設打開)— 否則同網段的裝置之間互相看不到。
從原始碼編譯(給開發者):
1# 1. 安裝 Flutter(用 fvm 鎖定版本,見 .fvmrc)
2brew install fvm # 或 https://fvm.app
3fvm install # 自動讀 .fvmrc
4
5# 2. 安裝 Rust
6curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
7
8# 3. 取得 source
9git clone https://github.com/localsend/localsend.git
10cd localsend/app
11
12# 4. 跑起來
13fvm flutter pub get
14fvm flutter run
注意:LocalSend 鎖 Flutter 版本(在
.fvmrc),系統全域 Flutter 版本不一定能編。用fvm flutter而不是flutter。
3. 核心架構解析
3.1 模組劃分
1localsend/
2├── app/ ← Flutter app(UI、平台整合、native 呼叫)
3│ ├── lib/ ← Dart 業務邏輯(pages, provider, widget, util)
4│ ├── android/ ios/ windows/ macos/ linux/ web/ ← 平台 wrapper
5│ └── rust/ ← Rust core 的 flutter_rust_bridge binding 入口
6├── core/ ← Rust 核心:HTTP server/client、TLS、WebRTC signaling、crypto
7│ └── src/
8│ ├── http/ ← REST API(v1 / v2 dto)+ rustls TLS
9│ ├── crypto/ ← cert generation / verify、token、nonce、hash
10│ ├── webrtc/ ← WebRTC signaling(v3 protocol,跨子網)
11│ └── model/ ← Device / Transfer / Discovery 資料模型
12├── common/ ← Dart 共用 DTO / model / multicast discovery / API route
13│ └── lib/
14│ ├── model/dto/ ← request/response DTO(與 Rust core 對齊)
15│ ├── api_route_builder.dart ← REST path /api/localsend/{v1,v2}/{info,register,...}
16│ └── constants.dart ← protocol version, port, multicast group
17├── server/ ← headless server 模式(無 UI)
18├── cli/ ← command line client
19└── submodules/ ← 鎖定的 flutter SDK
3.2 P2P Discovery + Transfer 流程
LocalSend 的核心可拆成 3 個階段:
階段 A:發現(Discovery)
- 每個裝置啟動時,加入 UDP multicast group
224.0.0.167:53317(這個位址在224.0.0.0/24範圍,是某些 Android 唯一能收的 multicast 範圍) - 主動廣播自己的
MulticastDto(含 alias / fingerprint / port / protocol version) - 也監聽其他裝置的廣播 → 維護本機 device list
- Fallback:若 multicast 被路由器擋掉,HTTP discovery(掃描子網 +
GET /api/localsend/v2/info)
階段 B:握手(Register / Prepare-Upload)
- 發送方點選目標裝置 + 選好檔案 →
POST /api/localsend/v2/prepare-upload- body 包含 device info + file metadata list + optional PIN
- 接收方 UI 跳「對方要傳這些檔案,接受?」彈窗 → 使用者按下 → server 回傳
sessionId + fileId→tokenmap
階段 C:實際傳輸
- 對每個 fileId,發送方
POST /api/localsend/v2/upload?sessionId=...&fileId=...&token=...,body 是檔案 stream - 接收方寫入 download 目錄
- 任一方可
POST /api/localsend/v2/cancel終止
3.3 完整序列圖
sequenceDiagram
autonumber
participant A as Device A (sender)
participant N as LAN multicast group
224.0.0.167:53317
participant B as Device B (receiver)
Note over A,B: Phase A — Discovery (UDP multicast)
A->>N: MulticastDto announce (alias, fp, port, v=2.1)
B->>N: MulticastDto announce
A->>B: (fallback) GET /api/localsend/v2/info
B-->>A: InfoDto (alias, fingerprint, deviceType)
Note over A,B: Phase B — Handshake (HTTPS REST)
A->>B: POST /api/localsend/v2/prepare-upload
{info, files[]}
Note right of B: UI 跳出「接受?」彈窗
可選 PIN 驗證
B-->>A: 200 OK {sessionId, files:{id→token}}
Note over A,B: Phase C — Transfer (one POST per file)
loop for each file
A->>B: POST /upload?sessionId&fileId&token
(stream body)
B-->>A: 200 OK
end
Note over A,B: Optional cancel
A->>B: POST /cancel?sessionId
3.4 TLS / 加密設計(重點)
- 每台裝置在第一次啟動時,本機產生 ECDSA self-signed 憑證(
core/src/crypto/cert.rs)— 私鑰存在本機加密 storage - HTTPS 流量用 rustls(不是 OpenSSL),server 與 client 都帶憑證
- 不依賴 CA 信任鏈 — 因為是自簽,瀏覽器/系統信任無意義
- 改用 public key pinning(公鑰固定):
- 首次握手時,把對方的 cert public key 存進本機 device map
- 之後每次連線都 verify「對方憑證的 public key 是否與第一次相同」
- 若不同 → MITM 嫌疑,拒絕連線
- 額外 fingerprint:device 用 cert 的 SHA-256 fingerprint 當 ID
4. 主要模組詳細用法
4.1 REST API 路由(common/lib/api_route_builder.dart)
| Route | v1 path | v2 path | 用途 |
|---|---|---|---|
| info | /api/localsend/v1/info | /api/localsend/v2/info | 取得對方裝置資訊 |
| register | /api/localsend/v1/register | /api/localsend/v2/register | HTTP discovery 註冊 |
| prepareUpload | /api/localsend/v1/send-request | /api/localsend/v2/prepare-upload | 發起傳檔(v1 path 名不同) |
| upload | /api/localsend/v1/send | /api/localsend/v2/upload | 實際傳檔 |
| cancel | /api/localsend/v1/cancel | /api/localsend/v2/cancel | 取消 |
| show | — | /api/localsend/v2/show | 顯示已收檔 |
| prepareDownload | — | /api/localsend/v2/prepare-download | 反向:對方來拿檔 |
| download | — | /api/localsend/v2/download | 反向下載 |
v2 多了
show / prepare-download / download三個 endpoint,支援「Share via link」反向模式。
4.2 Multicast Discovery(common/lib/src/task/discovery/multicast_discovery.dart)
- 綁定 UDP
53317 - 加入 multicast group
224.0.0.167 - 監聽 + 解析
MulticastDto,更新 device list sendAnnouncement()主動播報,新進裝置會立刻發一次
4.3 Rust core HTTP server(core/src/http/server/mod.rs)
- 用
hyper+rustls+tokio - 同時綁 IPv4
0.0.0.0:53317與 IPv6[::]:53317 - nonce-based replay 防護(received/generated nonce LRU cache,size=200)
- 支援 legacy mode(向後相容 v1 client)
4.4 Flutter app(app/lib/)
| 子目錄 | 角色 |
|---|---|
pages/ | UI 頁面(settings, tabs, troubleshoot, debug, about, donation) |
provider/ | Riverpod state providers(network, selection, logging) |
widget/ | 共用元件 |
util/native/ | 平台 native 呼叫(檔案儲存、剪貼簿、tray、autostart、process 呼叫) |
rust/api/ | flutter_rust_bridge 自動產生的 Dart binding(呼叫 Rust core) |
5. 應用場景
| 場景 | 為什麼選 LocalSend |
|---|---|
| 跨 OS 桌機 ↔ 手機搬檔 | 沒 USB cable,AirDrop 只在 Apple 生態內;LocalSend 真跨平台 |
| 公司資料禁雲端外傳 | 純 LAN、不上 internet、可關閉加密以追蹤封包(合規場景) |
| 同事間快速共享 PDF / 圖 | 不必 Slack / Teams 上傳大檔 |
| NAS / Raspberry Pi 取/送檔 | 用 cli/ 或 server/ 模式跑 headless |
| Demo 場合(簡報 / 投影片) | 比 USB 隨身碟快;現場觀眾掃 QR 加入 share via link |
| 不適合的場景 | 跨網際網路(沒 relay);做檔案同步(這是 syncthing 的範疇);做大規模分發(用 BT / IPFS) |
6. 資安掃描報告
6.1 評估範圍
LocalSend 是 P2P 跨裝置檔案傳輸工具,其資安需求集中在 (1) 網路通訊機密性、(2) 對方身份驗證、(3) 接收端授權、(4) 檔案系統權限邊界、(5) 平台原生 API 呼叫。
6.2 風險逐項評估
| 維度 | 等級 | 說明 |
|---|---|---|
| TLS 加密強度 | 🟢 低 | rustls + ECDSA 自簽憑證 + 公鑰固定(pinning)。設計符合教科書 P2P 標準;可被使用者手動關閉加密(明文 HTTP)以利除錯,這在公開網段需謹慎 |
| 身份驗證(peer auth) | 🟢 低 | 用 cert public key fingerprint 當 ID;首次連線即綁定(TOFU 模型)。MITM 在「首次握手」窗口仍有理論風險 |
| 接收端授權 | 🟢 低 | 所有 prepare-upload 都會跳 UI 彈窗讓使用者點「接受」;可設 PIN 強制驗證;不存在「靜默接收」模式 |
| Replay 防護 | 🟢 低 | nonce LRU map(received + generated),size=200 |
| 網路綁定範圍 | 🟡 中 | 同時 listen 0.0.0.0:53317 + [::]:53317;多網卡裝置(VPN / docker bridge)可能讓 LocalSend 暴露於非預期介面。提供 networkWhitelist / networkBlacklist 設定可緩解 |
| Multicast discovery 隱私 | 🟡 中 | 在 LAN 內任何人都能看到 MulticastDto(alias、device type、fingerprint),等同公開廣播身份。在公共 Wi-Fi(咖啡店)建議關掉發現功能或改用 share-via-link |
| 檔案系統權限 | 🟡 中 | Android READ/WRITE_EXTERNAL_STORAGE、REQUEST_INSTALL_PACKAGES、QUERY_ALL_PACKAGES 等敏感權限;iOS NSLocalNetworkUsageDescription。REQUEST_INSTALL_PACKAGES 在 Android 上意味可裝 APK — 接收 APK 時應確認來源 |
| Process.run 呼叫 | 🟡 中 | app/lib/util/native/cmd_helper.dart 呼叫 powershell 處理 Windows 整合;troubleshoot_page.dart 用 runInShell: true 跑診斷指令。已檢視:參數陣列化、無 user input concat,shell injection 風險低 |
| 檔案刪除 | 🟢 低 | 僅刪除自家目錄下檔案(autostart config、temp);無任意路徑 delete |
| 密鑰儲存 | 🟢 低 | 私鑰由本機儲存(Dart StoredSecurityContext model),未發現上傳行為 |
| 依賴鏈 | 🟢 低 | Apache-2.0,主要依賴:flutter、rustls、hyper、tokio、x509-parser。皆主流維護中 |
| 自動更新 | 🟡 中 | 官方明說「app 沒有 auto-update」,必須走 store / package manager。長期不更新的使用者會錯過安全修補 |
6.3 綜合結論
🟢 整體資安等級:低風險,可安心於受信任 LAN 使用。
關鍵設計亮點:
- ✅ rustls + 自簽 ECDSA + key pinning:教科書級 P2P 加密
- ✅ 所有接收都需明確使用者授權;無靜默後門
- ✅ nonce + LRU 防 replay
- ✅ 開源 + Apache-2.0 + 公開 protocol 文件可審計
需要注意:
- ⚠️ 公共 Wi-Fi 場景:multicast announce 暴露 device fingerprint,建議關閉發現
- ⚠️
REQUEST_INSTALL_PACKAGES權限:收到 APK 時切勿無腦接受 - ⚠️ Android
QUERY_ALL_PACKAGES:Play Store 政策敏感權限,使用者應確認用途(多半是 share intent 整合用) - ⚠️ 多網卡裝置:用
networkWhitelist鎖介面,避免從 VPN / docker 暴露
7. FAQ
Q: 為什麼對方看不到我? A: 三大原因。(1) 防火牆擋 UDP/TCP 53317;(2) Wi-Fi router 開了 AP isolation;(3) Windows 網路設定為「公用」(私人比較寬鬆)。macOS / iOS 還要去設定 → 隱私 → 本機網路打勾。
Q: 為什麼速度很慢? A: 用 5 GHz Wi-Fi;雙方都關掉加密(會用明文 HTTP,僅在信任的 LAN 用);Android 接收端有已知 SAF stream 慢的 issue。
Q: 可以跨網際網路用嗎?
A: 不能直接用 — LocalSend 不跑 relay server。可以用:(1) Tailscale / ZeroTier 把兩端拉進同一 virtual LAN;(2) v3 protocol 提供的 WebRTC signaling 模式(experimental,core/src/webrtc/)。
Q: 自簽憑證安全嗎? A: 安全 — 因為配 key pinning。第一次連線後,對方的 public key 就被綁住,往後 MITM 會被偵測。風險只在「第一次握手的瞬間」,這是 TOFU(trust on first use)模型的本質限制。
Q: 為什麼有 Rust core?Flutter 不夠嗎? A: 近期路線(2026-03 commits)是把 HTTP client/server 從 Dart 遷到 Rust,原因:(1) 跨平台統一行為;(2) rustls 比 Dart 內建 TLS 更可控;(3) 效能(特別是大檔 streaming)。
Q: Web 端能用嗎?
A: 有 app/web/,但 browser 無法綁 53317 port、無法 multicast,所以 web 端只能當 client 連到別人的 LocalSend(透過 share-via-link),不能當 server。
8. 進階技巧
8.1 Portable mode(攜帶式)
在執行檔同目錄建一個空的 settings.json → app 會把設定存到這檔,而不是 OS 預設位置。適合放隨身碟、跨機共用設定。
8.2 Hidden startup(背景啟動)
1localsend_app.exe --hidden
只在 system tray,無視窗 — 適合掛機當「接收伺服器」。
8.3 CLI 模式
cli/ 目錄有純命令列版本:
1dart pub get
2dart run cli/bin/cli.dart # 細節參考 cli/README.md
8.4 鎖定網路介面
在 settings 把 networkWhitelist 設成你的實體 Wi-Fi/eth IP 範圍,避免 LocalSend 從 VPN / docker bridge 介面暴露。
8.5 反向接收(Share via link)
v2 protocol 加的 show / prepare-download / download 三個 endpoint:發送方產 link,接收方在瀏覽器打開該 link 從發送方拉檔。對「web only」對方裝置很實用。
9. 整合進其他工作流
| 場景 | 怎麼做 |
|---|---|
| AI Knowledge Template 跨機同步 inbox/ | 在 NAS 跑 server/ headless mode;本機跑 client,自動接收 daily fetch 後的 markdown |
| 分析師 mobile ↔ workstation 互傳 | mobile 拍照 / 截圖 → LocalSend 一鍵推到 workstation 的 inbox/Image/ |
| demo 影片 / paper PDF 投影現場分發 | 開啟 share-via-link,投影 QR code,現場觀眾掃碼下載 |
| 避免 Slack / Teams 大檔上傳 | 公司內部資料只走 LAN,不過外部 SaaS |
| 替代 SFTP / SCP 的輕量方案 | 不必開 ssh、不必管 key 分發,UI 點兩下就好 |
10. Checklist(自評清單)
- 已用 store / package manager 安裝(非裸 release)
- 防火牆放行 TCP+UDP 53317
- Wi-Fi router 關閉 AP isolation
- Windows 將網路設為「私人」
- macOS / iOS 設定 → 隱私 → 本機網路打勾
- 設好 device alias(不要用預設 hostname,避免洩漏)
- 開啟 PIN(強化首次握手安全)
- 公共 Wi-Fi 場景:關閉「自動發現」或改用 share-via-link
- 多網卡裝置:設好
networkWhitelist - 確認接收的不是來路不明的 APK
- 定期手動更新(沒有 auto-update)
11. 進一步閱讀
- 官方協定文件:https://github.com/localsend/protocol — 對協定設計感興趣的必讀
- README:本 repo 根目錄(多語版本在
readme_i18n/) - CONTRIBUTING:https://github.com/localsend/localsend/blob/main/CONTRIBUTING.md
- 入口檔速查:
common/lib/constants.dart— protocol version / port / multicast groupcommon/lib/api_route_builder.dart— REST 路由 v1/v2 對照core/src/http/server/mod.rs— Rust HTTP server + TLS 主入口core/src/crypto/cert.rs— 憑證生成 / 驗證 / 公鑰比對core/src/model/discovery.rs— DeviceType enumapp/lib/util/native/— 各平台原生整合app/android/app/src/main/AndroidManifest.xml— Android 權限清單
教學完成度說明:本教學以協定 + 資安為主軸,UI / 翻譯 / store 上架等與「資料工程師如何使用 LocalSend」較無關的細節從略。若需深入 Flutter UI 實作,建議直接讀
app/lib/pages/與app/lib/widget/;若需深入 Rust core,從core/src/http/server/mod.rs與core/src/crypto/開始最快。
Comments