统信UOS下LocalSend实战:局域网文件传输与剪贴板同步

发布时间:2026/9/18 7:59:26
统信UOS下LocalSend实战:局域网文件传输与剪贴板同步
localsend 这个开源小工具我最初只把它当成局域网里的隔空投送手机拍的照片拖到统信UOS电脑上或者把UOS里的合同稿甩到同事的Windows机器上点两下确认就完事。用得久了才发现只把它当文件传输用大概只用掉了它三分之一的能力。它本质是一套跑在局域网里的 HTTP 服务加设备发现机制理解了这一层你就能把文本、剪贴板、甚至多台设备之间的自动流转都串起来——这也是我最近在统信UOS上折腾得最多的部分。这篇内容适合三类人看一是在信创环境里办公、手里同时有UOS、Windows、安卓、iOS好几台设备的人二是嫌微信传文件、U盘拷贝、网盘中转太啰嗦的人三是愿意动一点命令行、想把手上的机器连成一张内网传送网的人。下面我按选型思路、环境搭好、文本与剪贴板玩法、多设备联动、问题排查、性能与安全这几个层面把我踩过的坑和真正跑通的方案完整写一遍。1. 我在统信UOS上留下的为什么是localsend而不是别的1.1 先摆清楚我的三个真实诉求我日常的工作环境有点杂主力机是装了统信UOS的台式机旁边一台Windows笔记本用来跑一些只在Windows上有的工具手机是安卓偶尔还要给用iPhone的同事发东西。我需要解决的问题其实就三个。第一是跨平台且不需要公网——很多传输方案要么只支持两个平台要么必须经过某个服务器中转一旦断网或者在内网隔离环境里就直接废掉。第二是不插U盘——U盘在两台机器之间来回插拔除了慢还容易带上不该有的东西而且UOS默认挂载U盘之后的权限和编码问题也挺烦。第三是要有文本通道——很多工作场景里我根本不需要传文件我只是想把手机上的一段地址、一串命令、一个验证码扔到电脑上或者把电脑上的一段日志扔到手机里。前两个诉求能筛掉一大半方案第三个诉求才是决定性的。localsend 恰好卡在这三点上完全跑在局域网不依赖任何外部服务器桌面端、移动端都有原生客户端而且它从 1.15 版本前后开始界面上就有了发送文本的入口接收端收到的东西可以直接复制走。这就是我留下它的直接原因。1.2 横向对比之后它的位置其实很清晰我把常用的几种方案放在一起对比过结论挺明显方案跨平台覆盖是否需要公网大文件速度文本/剪贴板通道我在UOS上的实际痛点U盘拷贝全平台否受U盘写入速度限制无反复插拔、挂载权限、文件名编码系统共享文件夹局域网内否接近网速上限无跨系统凭据配置麻烦手机端基本没法用网盘中转全平台是受上行带宽限制有但要走云端上传等待、隐私顾虑、大文件限速即时通讯软件全平台是一般有压缩图片、文件过期、跨账号登录麻烦蓝牙传输部分平台否非常慢无配对麻烦几百MB就开始磨人localsend全平台否接近网速上限有且可脚本化首次配置和防火墙需要注意从表里能看出来localsend 真正不可替代的地方不在传文件这一项因为系统共享文件夹在速度上同样能打。它的价值在于同时覆盖了文件通道和文本通道并且把这件事做成了跨平台一致的操作体验。手机上没有 SMB 客户端也不影响装个 localsend 就能和UOS对话。1.3 理解它的底层机制后面的玩法才有根很多人用 localsend 用不出花样是因为把它当成一个黑盒。其实它的结构很朴素客户端本体是一个 Flutter 应用桌面端和移动端共享同一套逻辑设备之间靠局域网组播互相发现我抓包看到的是224.0.0.167:53317这个组播组不同大版本可能调整过以你实际安装的版本为准确认之后双方通过HTTP 的 REST 接口完成握手和文件传输默认监听在 TCP 53317 端口上也可以在设置里切换成带自签证书的 HTTPS 模式。这两个事实决定了它所有的隐藏玩法既然是 HTTP那么任何能发 HTTP 请求的东西——curl、Python、shell 脚本——都能当它的客户端不必依赖图形界面既然是组播发现那么只要网络环境允许组播通过同一个网段里的设备就能自动互相看见。注意localsend 的接口路径在不同大版本之间改过v1 和 v2 的端点名字不一样。下面出现的接口路径是我在自己环境里验证过的写法你照抄之前建议先对着自己装的版本确认一遍别直接拿去套。2. 在统信UOS上把它装好、跑通、常驻后台2.1 安装路径的三条路按顺序试统信UOS的软件来源和外网 Linux 发行版不完全一样我按成功率排了个顺序给你。第一条路是系统自带的应用商店。打开商店搜一下如果有就直接装这是最省事的安装之后启动器里会有图标依赖也由系统处理好。缺点是有时候商店里的版本偏老剪贴板相关的新功能可能还没有。第二条路是官方发布的 deb 包。先去确认自己的架构UOS 有 x86_64 和 arm64 两种常见形态uname -m # x86_64 就是 amd64 包aarch64 就要下 arm64 的包下载对应架构的 deb 之后sudo dpkg -i localsend_*.deb # 如果提示依赖缺失用下面这条补齐 sudo apt -f install第三条路是AppImage适合 deb 装不上或者你没有管理员权限的情况chmod x LocalSend-*-x86_64.AppImage ./LocalSend-*-x86_64.AppImageAppImage 的好处是绿色、可携带把文件放到家目录里就能跑卸载就是删文件。代价是它不会自动注册图标和文件关联需要你自己做桌面快捷方式。2.2 端口、防火墙与网络环境这三件事决定成败装完打不开设备列表的概率八成出在这三件事上。第一件是防火墙。UOS 默认不一定开着防火墙但如果你的机器上装了并启用了ufw53317 会被直接挡掉。放行的命令是sudo ufw allow 53317/tcp sudo ufw allow 53317/udp sudo ufw status verboseTCP 负责真正的数据传输和接口调用UDP 负责组播发现两个都得放只放一个的典型症状是能看见对方但传不过去或者能传但设备列表里没对方。第二件是网络环境。如果你的UOS和手机连的是不同的 SSID那它们物理上就不在一个广播域里组播过不去。企业Wi-Fi常见的访客网络隔离AP隔离也会造成同样的问题——同一台路由器下两台设备互相 ping 得通但组播被设备侧的隔离策略丢掉了。这种环境下只能换到同一个普通网络或者走下一节要讲的直连IP方案绕开发现环节。第三件是多网卡。UOS台式机常常同时有有线网卡和无线网卡还可能有虚拟机网桥、Docker网桥。localsend 一般会挑一个网卡做广播如果它挑错了那张虚拟网卡你在真实网络里就永远发现不了对方。我的做法是把不用的网卡在系统设置里临时关掉只留一张在用的确认能通之后再做其他配置。这一步比去翻它的配置文件要快得多。2.3 五分钟自检流程按顺序排掉常见故障出问题的时候我不建议瞎试按下面这个顺序走一遍五分钟之内一定能定位到层。# 1. 进程在不在端口有没有在监听 ss -tulnp | grep 53317 # 2. 本机IP和对方是不是同一个网段 ip -4 addr show | grep inet ping -c 3 对方IP # 3. 从本机直接打对方的接口看服务活没活 curl -s -m 3 -X POST http://对方IP:53317/api/localsend/v2/info \ -H Content-Type: application/json -d {fingerprint:probe}第三条命令如果能返回一段 JSON说明对方的 localsend 服务和网络层都是通的问题就只剩下发现环节——也就是组播没过去。这时候你可以在双方设置里检查一下设备别名确认看到的确实是对方而不是同一台机器的两个实例。提示同一台机器上如果你既开了系统商店装的版本又跑了 AppImage 版本两个实例会抢同一个端口。表现出来的现象很诡异图标点开是空白的、设备列表里出现一个同名设备。排查时先用ps aux | grep -i localsend看一眼有几个进程。3. 隐藏玩法一把文本和剪贴板接到一起当速记本用3.1 先从界面里本来就有的文本发送说起打开 localsend 的发送页面除了选文件还有文本类的入口。你可以直接把一段文字粘进去发出去对方点接收之后会收到一份纯文本可以直接复制使用。这个功能本身不稀奇真正有用的是它带来的几个细节接收端不会像聊天软件那样自动加链接、不会压缩你的内容、也不会在几天之后把文件清理掉。我经常用它把手机上看到的一段长命令、一个下载地址、一段配置片段扔到UOS上然后直接在终端里粘贴执行。版本差异要注意有些版本里文本是作为.txt文件落地到接收目录的有些版本里是一个可以直接长按复制的气泡。两种都能用只是后续脚本的处理方式不同——落地成文件的可以配合监控脚本做自动化气泡形式的就只能手动复制。3.2 真正双向的剪贴板同步靠两个小脚本界面上点来点去还是太低效我想要的是在UOS上按下快捷键当前剪贴板内容自动出现在手机上手机上复制一段东西UOS这边剪贴板自动更新。这个目标可以拆成两个方向分别实现。发送方向用轮询加防抖来做最稳。UOS 的桌面环境默认是 X11 会话所以xclip可以直接用。先把工具装上sudo apt install xclip inotify-tools然后是发送脚本#!/usr/bin/env bash # ~/bin/localsend-clip-send.sh set -euo pipefail TMP_DIR${XDG_RUNTIME_DIR:-/tmp}/localsend-clip mkdir -p $TMP_DIR LAST_FILE$TMP_DIR/last.txt STAGE$TMP_DIR/stage.txt # 1. 读出当前剪贴板DDE 默认 X11 会话 if command -v xclip /dev/null 21; then xclip -selection clipboard -o $STAGE 2/dev/null || exit 0 else xsel --clipboard --output $STAGE 2/dev/null || exit 0 fi # 2. 空内容、超大内容直接跳过避免把整张截图当文本发出去 size$(stat -c %s $STAGE 2/dev/null || echo 0) if [ $size -eq 0 ] || [ $size -gt 262144 ]; then exit 0 fi # 3. 和上一次比对没变化就不重复发送 if [ -f $LAST_FILE ] cmp -s $STAGE $LAST_FILE; then exit 0 fi cp $STAGE $LAST_FILE # 4. 调发送脚本接收端IP从配置文件读避免硬编码 exec python3 $HOME/bin/localsend_send_text.py $STAGE这个脚本里有三个设计点值得说清楚。大小阈值是为了防止你把一张复制的图片或者一段巨大的日志误发出去262144 字节256KB对纯文本来说已经非常宽裕。内容比对是为了防抖因为剪贴板监听往往会在短时间内触发多次事件不做比对就会连发好几遍。把接收端IP抽到配置里是为了以后换机器不用改脚本。配合一个每两秒跑一次的定时任务就够用了# crontab -e 之后加一行 */1 * * * * DISPLAY:0 XAUTHORITY$HOME/.Xauthority /home/你的用户名/bin/localsend-clip-send.sh /dev/null 21注意crontab里的环境变量是干净的DISPLAY和XAUTHORITY必须手动带上否则xclip会因为找不到 X 会话而报错。这是我在这上面卡了最久的一个点报错信息还特别含糊。接收方向更简单用文件监控就够了。接收端把文本类的文件单独存到一个目录然后盯着这个目录#!/usr/bin/env bash # ~/bin/localsend-clip-watch.sh set -euo pipefail SAVE_DIR$HOME/Downloads/LocalSend inotifywait -m -e close_write --format %f $SAVE_DIR | while read -r f; do case ${f,,} in *.txt|*.md|*.log|*.json|*.yaml) sleep 0.3 # 等文件句柄彻底释放避免读到半截内容 if xclip -selection clipboard -i $SAVE_DIR/$f 2/dev/null; then notify-send localsend 已把 $f 写入剪贴板 fi ;; esac doneclose_write这个事件要在写完之后才触发但有时候文件系统缓存还没落盘所以加一个 0.3 秒的等待更稳妥。notify-send会弹一个系统通知让你知道剪贴板被换掉了——这一步别省不然你粘贴出来的东西和你以为的不一样时会非常懵。3.3 不想依赖图形界面直接打它的HTTP接口上面两个脚本的共同前提是发送端得有个 localsend 图形界面在跑。如果你的场景是纯命令行、或者想把发送能力嵌到别的工具里那就直接走接口。它的传输流程大致是三步先向接收端声明自己的身份再申请一次上传会话拿到令牌最后带着令牌把内容推上去。步骤接口路径以 v2 为例作用典型返回1/api/localsend/v2/register向对方广播自己的设备信息对方设备信息2/api/localsend/v2/prepare-upload提交文件清单申请上传令牌sessionId 和每个文件的 token3/api/localsend/v2/upload带 sessionId、fileId、token 推送二进制内容状态码4/api/localsend/v2/cancel中途放弃时通知对方清理会话状态码先用curl手工打一次把流程摸清楚curl -s -X POST http://192.168.1.23:53317/api/localsend/v2/prepare-upload \ -H Content-Type: application/json \ -d { info: { alias: uos-cli, version: 2.0, deviceModel: UOS Desktop, deviceType: desktop, fingerprint: uos-cli, port: 53317, protocol: http, download: false }, files: { 1: { id: 1, fileName: note.txt, size: 5, fileType: text/plain, sha256: 请填入文件的sha256 } } }prepare-upload的返回里会有sessionId和files字段files里就是你上一步传进去的 fileId 对应的令牌。拿到令牌之后再上传curl -s -X POST http://192.168.1.23:53317/api/localsend/v2/upload?sessionId会话IDfileId1token令牌 \ -H Content-Type: application/octet-stream \ --data-binary note.txt -o /dev/null -w %{http_code}\n手工跑通之后用 Python 包一层就更顺手了#!/usr/bin/env python3 # ~/bin/localsend_send_text.py import hashlib, json, os, sys, urllib.parse, urllib.request, uuid RECEIVER_IP 192.168.1.23 RECEIVER_PORT 53317 BASE fhttp://{RECEIVER_IP}:{RECEIVER_PORT} def post_json(url, payload, timeout10): data json.dumps(payload).encode(utf-8) req urllib.request.Request( url, datadata, methodPOST, headers{Content-Type: application/json}) with urllib.request.urlopen(req, timeouttimeout) as r: body r.read() return json.loads(body) if body else {} def local_info(): return { alias: uos-clip-bridge, version: 2.0, deviceModel: UOS Desktop, deviceType: desktop, fingerprint: uos-clip-bridge, port: 53317, protocol: http, download: False, } def send(path): content open(path, rb).read() if not content: return name os.path.basename(path) or clipboard.txt file_id uuid.uuid4().hex[:8] files { file_id: { id: file_id, fileName: name, size: len(content), fileType: text/plain, sha256: hashlib.sha256(content).hexdigest(), } } resp post_json(f{BASE}/api/localsend/v2/prepare-upload, {info: local_info(), files: files}) token resp[files][file_id] query urllib.parse.urlencode({ sessionId: resp[sessionId], fileId: file_id, token: token}) req urllib.request.Request( f{BASE}/api/localsend/v2/upload?{query}, datacontent, methodPOST, headers{Content-Type: application/octet-stream}) with urllib.request.urlopen(req, timeout60) as r: print(r.status) if __name__ __main__: send(sys.argv[1])这段脚本能跑通的前提是接收端必须处于自动接收状态。localsend 的设置里通常有一个快速保存或者始终接受的开关打开之后它才会跳过人工确认、直接把内容收下。如果不打开脚本发出去的会话会一直停在等待对方确认最后超时。另外两个细节sha256字段在部分版本里是必填的缺了会直接被拒fileId用短随机串是为了避免多次请求撞号。这些都是我实际调试时撞出来的文档里不一定写。4. 隐藏玩法二把UOS当传输中枢做多设备联动4.1 先把设备别名和指纹整理清楚设备一多最大的麻烦不是传输本身而是认错机器。我的做法是给每台设备起一个一眼能看懂的名字规则是位置-角色比如书房-UOS台式、客厅-win本、随身-安卓、测试-iOS。别用默认的主机名因为很多机器的主机名是一串随机字符在设备列表里根本分不清。localsend 在传输前会展示对方的指纹信息这个指纹是设备身份的一部分。同一台机器重装系统或者清理过应用数据之后指纹会变这时候对方设备列表里会多出一个陌生设备别慌删掉旧的记录就行。提示如果在UOS上同时跑着多个网络接口localsend 显示的别名可能带上网卡后缀。整理别名的时候建议先把多余的网卡关掉再改名字否则你会看到两台同名不同后缀的设备很容易点错。4.2 三台以上设备的典型编排我现在的日常流是这样的手机在客厅随手拍的照片直接在手机上选localsend发给书房-UOS台式UOS这边因为开了自动接收文件静默落到~/Downloads/LocalSend我在UOS上整理好的一批素材通过局域网发给客厅-win本速度能跑满千兆给用iPhone的同事发东西时让对方装同一个客户端同一个网络下就能直接点对点不需要他注册任何账号。这套流程里最关键的其实是接收目录的设计。localsend 默认把收到的文件放在下载目录下的一个子文件夹里如果所有类型的文件都堆在一起几天之后就是一团乱麻。我的做法是在设置里把保存目录指到一个固定位置然后在接收侧用脚本按扩展名分流#!/usr/bin/env bash # ~/bin/localsend-sort.sh set -euo pipefail SRC$HOME/Downloads/LocalSend INBOX$HOME/ls-inbox mkdir -p $INBOX/{text,image,archive,other} inotifywait -m -e close_write --format %f $SRC | while read -r f; do src$SRC/$f [ -f $src ] || continue case ${f,,} in *.txt|*.md|*.json|*.yaml|*.log) mv -f $src $INBOX/text/ ;; *.png|*.jpg|*.jpeg|*.webp|*.gif) mv -f $src $INBOX/image/ ;; *.zip|*.tar.gz|*.7z|*.rar) mv -f $src $INBOX/archive/ ;; *) mv -f $src $INBOX/other/ ;; esac done文本类落到text/之后再配合上一节的剪贴板脚本就形成了一条完整的链路手机复制一段文字 → 发给UOS → 自动落到文本目录 → 自动写入剪贴板 → 我在终端里直接粘贴。这套东西跑顺之后跨设备复制粘贴这件事基本就感觉不到存在了。4.3 让它在后台常驻别每次都手动开想让上面这些脚本一直生效localsend 本身必须常开。UOS 上比较稳的做法是用用户级的 systemd 服务# ~/.config/systemd/user/localsend.service [Unit] Descriptionlocalsend clipboard bridge Aftergraphical-session.target [Service] Typesimple EnvironmentDISPLAY:0 EnvironmentXAUTHORITY%h/.Xauthority ExecStart%h/bin/localsend-clip-send-daemon.sh Restarton-failure RestartSec5 [Install] WantedBydefault.targetsystemctl --user daemon-reload systemctl --user enable --now localsend.service systemctl --user status localsend.service如果你希望这台机器即使没人登录也保持服务运行还需要打开 lingersudo loginctl enable-linger $USER不想碰 systemd 的话也可以在~/.config/autostart/下放一个.desktop文件效果是登录后自动启动简单直接代价是启动顺序不好控制。我一般脚本类的用 systemd图形客户端本体用 autostart各取所长。5. 常见问题与排查技巧实录5.1 一张速查表覆盖我遇到过的九成情况现象最可能的原因我的处理动作设备列表里完全看不到对方组播被挡、不在同一网段、防火墙先 ping 通再放行 53317/TCP 和 UDP单向可见A能看到BB看不到AA侧防火墙只放行了出站两台机器都检查一遍入站规则能看到设备但发送一直转圈端口被占用或接收端未响应ss -tulnp | grep 53317确认监听状态传输到一半中断无线网卡省电、休眠、漫游切AP接有线或关掉无线省电模式大文件传过去损坏传输过程中断重连、磁盘写满核对文件大小检查目标分区剩余空间中文文件名变成乱码接收端编码或旧版本兼容问题升级到较新版本或先压缩再传脚本发送一直停在等待确认接收端没开自动接收打开快速保存/始终接受类开关同一台机器出现两个同名设备同时跑了两个客户端实例杀掉多余进程只留一个接口返回 4xx协议版本不匹配v1/v2混用用curl打 info 接口确认版本或换端点重试关机重启后脚本失效环境变量缺失、服务没 enable检查DISPLAY、XAUTHORITY确认服务已开机自启5.2 三个我印象最深的坑第一个坑手机能看到UOSUOS看不到手机。当时排查了很久最后发现是UOS上装过的一堆虚拟化软件留下了好几张虚拟网卡localsend 广播的时候选了虚拟网卡结果组播信息根本没进到真实局域网。解决办法很土把不用的虚拟网卡在系统设置里临时关掉重启客户端马上就通了。从那以后我养成一个习惯只要设备发现出问题第一步就是ip -4 addr看一眼到底有几张网卡。第二个坑传大文件传到 80% 断掉。一开始我以为是网络问题换了好几次路由器设置都没用。后来发现是无线网卡的省电策略在空闲时把连接降级了而 localsend 的传输在中间会有短暂的空档正好撞上。关掉无线省电或者干脆接一根网线问题就消失了。凡是超过 1GB 的传输我现在的原则是能接有线就接有线省下的时间远比插根线多。第三个坑脚本在终端里跑得好好的加到 crontab 里就报错。这是最经典的定时任务环境变量问题。终端里DISPLAY是继承来的定时任务里什么都没有xclip找不到 X 会话就静默失败。解决办法就是前面写的那样把DISPLAY和XAUTHORITY显式带上并且把标准错误重定向到日志文件不然你只能看到它没生效完全不知道为什么。5.3 三条我觉得最值钱的经验别急着写脚本先手工跑通一次。我见过不少人一上来就写自动化结果接口版本不对、接收端没开自动接收折腾一天没结果。正确的顺序永远是图形界面手动传一次成功curl手工传一次成功最后才上脚本。把日志写下来。所有后台脚本第一行就该是exec $HOME/.local/share/localsend-bridge.log 21不然出问题时你就只能靠猜。我现在给每个脚本都留了独立日志出问题直接tail -f看比什么都快。接收目录一定要重新指。默认的下载目录在多用户机器上经常有权限问题而且会和系统下载的东西混在一起。我最开始就是没改目录结果剪贴板脚本读到了一堆无关的下载文件剪贴板被莫名其妙地覆盖了好几次。把接收目录单独指到~/ls-inbox这类位置问题一次性解决。6. 速度、安全与长期使用上的几个取舍速度这块localsend 本身不是瓶颈瓶颈在网络。千兆有线环境下传输速度基本能贴着网卡上限跑我实测传一个 2GB 左右的素材包稳定在 100MB/s 上下换成 5GHz Wi-Fi同样大小的文件速度会掉到 30MB/s 到 60MB/s 之间还会因为信号波动而起伏。所以如果你有大批量文件要来回倒把UOS台式机接上网线是最划算的优化比调任何参数都有效。至于客户端设置里的并发数量之类的选项我建议保持默认改大了在小内存机器上反而容易出问题。安全边界这件事用起来舒服和管得严是矛盾的我的做法是分场景处理。在自己家里的内网我开着自动接收图的是省事在公司或者公共场所的网络我一定把自动接收关掉每次都手动确认并且看一下对方设备的指纹是不是我认识的那台。localsend 支持切换成 HTTPS 模式用的是自签证书这在纯内网环境里主要意义是防止内容被同一网络里的其他设备嗅探但它不能替代身份认证——真正的身份确认还是靠指纹核对。还有一点容易被忽略如果你在公共网络上开着自动接收任何人都可能往你的下载目录里塞文件这跟开着一个匿名投递箱没什么区别。最后说一点长期使用上的体会。localsend 这类局域网工具的寿命其实取决于它会不会被过度配置。我一开始也想搞一套完美方案设备自动发现、剪贴板双向同步、文件自动分类、开机自启全套自动化。后来发现真正高频用到的只有两件事——手机和电脑之间的文本互传以及两台电脑之间的大文件搬运。其余的自动化都属于锦上添花配置越复杂出问题时越难定位。所以我现在的做法是核心链路接收目录、自动接收开关、剪贴板脚本保持极简附加的分流脚本单独跑、随时能停任何一个环节出问题都不影响主流程。这套东西我在UOS上跑了小半年中间只因为版本升级调整过一次接口路径其余时间基本是装上就忘的状态——对工具来说这可能就是最好的评价。