LocalSend 1.18.2 是局域网传文件工具,Windows 包 26113764 字节,解开 53 个文件共 65482435 字节,启动后监听 53317 端口。

它要解决的是:两台机器在同一个网里,想传一个 500 MB 的文件。微信压图、QQ 限速、网盘要上传再下载、U 盘要跑一趟,这些都比文件本身慢。

这次量了四件事:包解开有多少文件、窗口多久出来、它自己占多少内存和句柄,以及它到底在网络里开了哪些端口。

53 个文件,65482435 字节

官方 zip 是 26113764 字节,解开 53 个文件。压缩比 2.5,不高,因为里面主要是已经编译过的二进制。

项值说明
压缩包26113764 字节windows-x86-64.zip,免安装
解压后65482435 字节53 个文件
localsend_app.exe220896 字节只是个启动器
data/app.so16073648 字节Flutter 编译产物,真正的主体

220896 字节的 exe 不是主程序

双击的那个 exe 只有 220896 字节,它的职责是加载 Flutter 引擎再把手交给 data/app.so。真正跑业务逻辑的代码在那 16073648 字节里,另外 20 个 DLL 又占掉 43578184 字节。

没有安装步骤

解压出来直接双击,不写注册表也不装服务。换机器把整个目录拷过去就能用,设置存在用户目录下的配置文件里。

20 个 DLL 占掉 66.5%

65482435 字节里,20 个 DLL 合计 43578184 字节,占 66.5%。真正属于 LocalSend 自己的逻辑只有 app.so 和那个 20265184 字节的 Rust 库。

文件字节是什么
flutter_windows.dll20819168Flutter 的 Windows 嵌入层
rust_lib_localsend_app.dll20265184协议与传输用 Rust 写
data/app.so16073648Dart 编译产物
MaterialIcons-Regular.otf1645184图标字体
data/icudtl.dat862304ICU 国际化数据
20 个 DLL 合计43578184占 66.5%

协议层是 Rust,界面层是 Dart

20265184 字节的 rust_lib_localsend_app.dll 说明文件传输和发现协议没有用 Dart 写,而是编译成 Rust 库再被 Flutter 调用。这解释了为什么 UDP 广播和 TCP 传输能贴着系统调用走。

29 个资源文件都在这

flutter_assets 目录里 29 个文件,最大的一个是 1645184 字节的图标字体。整套界面图标都从这一个字体文件里取,没有散落的 png。

窗口 2559 毫秒出来

从启动到窗口可见用了 2559 毫秒。这个数字比同类的原生程序慢一个数量级,因为要先初始化 Flutter 引擎再把界面画出来。

指标值
首帧2559 毫秒
主程序data/app.so 16073648 字节
解压后目录65482435 字节

第一次启动还要更久

2559 毫秒是冷启动的数据,包含读 65482435 字节目录的时间,其中 20 个 DLL 就有 43578184 字节。第二次启动文件已在系统缓存里,会明显缩短。

它常驻在后台

关掉窗口后进程还在,否则别的设备就找不到它。要让它彻底退出得在托盘图标上右键退出,或者在任务管理器里结束进程。

占 132 MB 内存

界面打开、等待接收的状态下测它自己:常驻内存 132 MB,769 个句柄,92 个线程。

指标值
常驻内存132 MB
句柄769 个
线程92 个

92 个线程是 Flutter 的常态

Flutter 引擎自带渲染线程、IO 线程和平台线程,加上应用自己的异步任务,空界面就有 92 个线程。这和原生程序十几个线程的规模不是一个量级。

132 MB 换的是跨平台一致

同一份代码在 Windows、macOS、Linux、Android、iOS 这 5 个平台跑,代价就是每个平台都要带一份 Flutter 引擎。想省内存得用原生重写。

监听 53317 端口

启动后它在网络上开了四个监听点:UDP 53317 的 IPv4 和 IPv6 各一个,TCP 53317 的 IPv4 和 IPv6 各一个,TCP 那两个处于监听状态。

协议地址端口状态
UDP0.0.0.053317监听
UDP::53317监听
TCP0.0.0.053317LISTEN
TCP::53317LISTEN
LocalSend 1.18.2 主界面:左侧接收 / 发送 / 设置三个入口,中间显示随机生成的设备名与圆形图标,底部有「通过链接接收」按钮

UDP 找设备,TCP 传文件

UDP 用来广播和发现同网段的其他设备,TCP 用来建立连接搬数据。两个都绑在 53317 上,靠协议区分。

端口被占就直接启动失败

53317 是写死的默认端口,没有自动换端口的开关。要同时跑两个实例做测试,得先改其中一个的端口设置,否则两个进程会抢同一个端口。

设备名是随机生成的

首次启动会给自己起一个随机设备名,这次分到的是「极好的莴苣」。这个名字会广播给同网段的其他设备。

界面元素作用
接收 / 发送 / 设置左侧三个入口,默认停在接收
设备名随机生成,可在设置里改
通过链接接收不用同一网段,靠分享链接建立连接

名字可以改

设置里有 1 个设备名输入框,改成「客厅台式」这类固定名字,别人才知道往哪台发送。默认的随机名字两台机器放一起很难区分。

链接接收是另一条路

同一网段靠 UDP 广播发现,不同网段走「通过链接接收」这条 HTTP 通道。两条路的底层不一样,前者要求两端都在同一广播域。

几个真踩过的点

下面三条在这次测试里真实出现,附当时的处理方式。

现象原因处理
启动脚本里 9 秒后进程消失窗口进程和后台服务是两个阶段用 tasklist 确认实际 pid,别只看创建时返回的那个
两个实例只能起一个都抢 53317 端口改其中一个的端口再启动
拖进窗口没有反应当前在接收页而不是发送页先切到发送再拖文件

端口冲突会直接失败

53317 是固定端口,没有多实例参数。同时开两个做测试前先确认端口没被占,否则第二个进程拿不到监听权。

如果同时启动两个实例,53317 端口只能被一个进程拿到,另一个会失败,此时得先改端口。

接收和发送是两个页面

默认停在接收页,此时拖文件进去不会有任何动作。要把界面切到发送这 1 个入口才能选择目标设备。

什么场景值得上

两台设备在同一个局域网、要传的东西超过几十 MB、又不想经过任何服务器时,它是最直接的一条路。

场景合适度说明
同一网段互传大文件高53317 端口直连,不经过服务器
手机和电脑互传高各平台同一份代码,界面一致
不同网段跨地传输中要用链接接收,速度取决于公网
常驻后台省内存低132 MB 常驻,比 U 盘方案贵

它不经过云端

数据只在 2 台设备之间走,没有中转服务器也没有账号。这是它和网盘方案最大的区别,也是它敢用在敏感文件上的理由。

授权与费用

MIT 许可,个人和商业环境都不收费。源码在 GitHub 上,用 Flutter 写的,release 覆盖 Windows、macOS、Linux、Android、iOS 五个平台。