惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Troy Hunt's Blog
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
B
Blog RSS Feed
S
Securelist
H
Help Net Security
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts
Spread Privacy
Spread Privacy
C
Check Point Blog
WordPress大学
WordPress大学
AWS News Blog
AWS News Blog
L
Lohrmann on Cybersecurity
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Privacy International News Feed
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
Blog — PlanetScale
Blog — PlanetScale
PCI Perspectives
PCI Perspectives
T
Tor Project blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Cyber Attacks, Cyber Crime and Cyber Security
Google Online Security Blog
Google Online Security Blog
The Hacker News
The Hacker News
宝玉的分享
宝玉的分享
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
Cisco Blogs
Last Week in AI
Last Week in AI
Webroot Blog
Webroot Blog
GbyAI
GbyAI
I
InfoQ
罗磊的独立博客
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
Latest news
Latest news
P
Palo Alto Networks Blog
博客园 - Franky
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security Affairs
H
Hacker News: Front Page
P
Privacy & Cybersecurity Law Blog
小众软件
小众软件
The Register - Security
The Register - Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
博客园_首页
aimingoo的专栏
aimingoo的专栏

Bottle小站

研二冬深再回首,前路依稀雾几重-Bottle小站 【食谱】投没投炒蛋-Bottle小站 秋风萧瑟天气凉,回首崎岖研一下-Bottle小站 广东惠州小游流水账-Bottle小站 初踏征程,研途漫漫-Bottle小站 博客十周年暨单身 23 周年纪念日-Bottle小站 【食谱】伪·吉野家牛肉饭-Bottle小站 【食谱】家常豆腐-Bottle小站 【小记】在 Google Colab 等平台上运行 GPU 容器-Bottle小站 【小记】Matplotlib 中设置自定义中文字体的正确姿势-Bottle小站 【小记】探探学习平台的字体混淆-Bottle小站 【食谱】麻婆豆腐-Bottle小站 春风轻捎信,佳音未负勉-Bottle小站 【斩虫】Hadoop中作业执行刚开始就挂掉的两种情况-Bottle小站 【小记】Docker容器间SSH公钥自动交换实现免密登录的一次尝试-Bottle小站 初试后,到达迷茫的路口-Bottle小站 寒风腊月起,回首大三上-Bottle小站 暑烧一夏,回顾一下(二)-Bottle小站 暑烧一夏,回顾一下(一)-Bottle小站
【小记】解决校园网中单播互通的不同子网间的 LocalSend 发现问题-Bottle小站
2026-01-18 · via Bottle小站

{(MainTitle)}

{(MainTitleEnd)} {(PostTitle)}【小记】解决校园网中单播互通的不同子网间的 LocalSend 发现问题{(PostTitleEnd)} {(PostDate)}20260117{(PostDateEnd)} {(PostContent)} 这篇文章也发布到了[咱的博客园站](https://www.cnblogs.com/somebottle/p/19497343/simple_report_of_localsend_switch)。 各位好久不见~下半年又是忙论文又是忙项目的,实在是没什么时间更新笔记了。趁着今天有点空闲,咱来写写最近抽空解决的一个小网络问题叭(゜ー゜)。 <bblock style='display:none'> { "src":"https://api.xbottle.top/ne/url/32235889", "cover":"https://api.xbottle.top/ne/cover/32235889", "title":"Last Song (Ending ver.)", "artist":"supercell", "loop":"false", "float":"right" } </bblock> ## 0. 问题背景 咱实验室有一台连接着打印机的计算机,我们在这台机器上挂了一个专门注册的 QQ 账号,需要打印文件时把文件发送到这个 QQ 账号上,在打印机计算机上下载下来就行了。 但是吧,像是比较机密文件的话,如果这样过一道别人的服务器感觉不太好,正好咱当时也找到了局域网文件传输工具 LocalSend,遂试了试。 结果我发现,打印机计算机上的 LocalSend 客户端无法发现我笔记本上的 LocalSend 端,反之亦然。我还得到打印机计算机上手动输入笔记本电脑被分配的 IP 地址才能传输文件,略显麻烦。 * 更不提校园网这有线网和无线网给设备分配的全是动态 IP,可能过一段时间就会变。 ![Analyzing](https://serv.xbottle.top/i/https://raw.githubusercontent.com/cat-note/bottleassets/main/img/BDE19D9F629D18E59D1752BDB5FDBFDE-2026-01-17.jpg) 分析了一下发现: 1. 没有办法互相发现是因为,我的笔记本接入的是校园网无线网络,而打印机计算机接入的是有线网络,两个主机在**不同的子网段**内,这阻隔了 LocalSend 发出的组播包 (可以参考 [LocalSend 协议](https://github.com/localsend/protocol))。 2. 可以手动输入 IP 地址来指定客户端传输文件,是因为 LocalSend 实际的传输等请求是**单播**的,而校园网在三层设备上有配置路由转发,所以单播包是可以互通的。 于是咱要解决的问题就是,如何在这种**多播 (组播) 隔离但是单播互通**的不同局域子网间实现 LocalSend 的发现功能。 ## 1. 问题描述 <img src="https://serv.xbottle.top/i/https://raw.githubusercontent.com/SomeBottle/localsend-switch/refs/heads/main/pics/issue_illustration.drawio.png" alt="Issue Illustration" width="600px" /> > Figure 1: 问题示意图。 可以看到 VLAN 0 中的 LocalSend 客户端无法成功发现 VLAN 2 中的 LocalSend 客户端,反之亦然。 LocalSend 客户端采用 UDP 组播来把自己的存在通告给局域网中其他客户端。然而,像校园网这种大型局域网,通常为了管理和减小广播域规模等目的,会将网络划分为多个 VLAN(虚拟局域网),对应多个子网,即使是现实中距离很近的两个设备,也有可能在不同的 VLAN 中。 * 比如我连接到校园网 WiFi 的笔记本电脑和连接有线校园网的实验室打印机电脑,虽然在同一间屋子,但就是处于不同网段的网络中。 不同子网之间的数据转发依赖于第三层路由设备来实现,不幸的是,LocalSend 向 `224.0.0.x` 组播地址及应用端口发送的 UDP 报文段是不会被三层设备转发的,而且其 TTL 值为 `1`,Wireshark 抓包如下: ![Wireshark Capture](https://serv.xbottle.top/i/https://raw.githubusercontent.com/SomeBottle/localsend-switch/refs/heads/main/pics/wireshark_captured.png) > Figure 2: Wireshark 抓包显示 LocalSend 发送的组播 UDP 报文段的 TTL 值为 1。 因此就有了明明两台设备近在咫尺,但是却没法互相发现对方 LocalSend 客户端的尴尬局面 ㄟ( ▔, ▔ )ㄏ。 ## 2. 解决问题 ### 2.0. 思路 尽管多播被隔离了,但是办公区校园网在三层配置上是会转发单播包的,我可以通过单播和不同的 VLAN 中的主机进行通信。 一个 LocalSend 客户端在尝试发现局域网内其他客户端时,会发送组播 UDP 包来声明自己的存在,其他客户端收到组播包后会通过**单播的 HTTP 请求**来在这个客户端上进行注册。因为单播可以跨 VLAN,所以这个注册操作是可以实现的,我可以**替 LocalSend 客户端向局域网内的其他 LocalSend 客户端发送注册请求**,从而实现跨 VLAN 的发现和注册。 从官方的协议文档可以看到 LocalSend 的通告包和注册请求的负载中都只有端口信息,没有源 IP 信息,**客户端在处理到来的请求时实际上是从网络层分组头部获取到源 IP 地址**的,因此这个请求必须从 LocalSend 客户端所处的主机上发出。为了实现这点,我可以**在每台有 LocalSend 的主机上都额外运行一个工具进程来代发注册请求**。 关键的问题来了,这些工具进程怎么知道局域网内其他 LocalSend 客户端的存在呢?其实我可以借助单播传输来实现这些工具进程之间的通信,从而让它们**互相交换**各自了解的 LocalSend 客户端信息。 为了解决动态 IP 的问题,我可以把其中一个或多个工具进程作为交换节点**部署在拥有静态 IP 的服务器**上(内网和外网的均可),然后让其他工具进程连接到这些交换节点,当交换过程收敛时,这些工具进程就能互相了解对方所处主机上的 LocalSend 客户端信息了(也让 LocalSend 客户端互相知晓了对方的存在)。 正好最近学了 Go 语言,照着上面这个思路实现下来,LocalSend Switch 这个工具就诞生辣!٩(>௰<)و * 简单来说 LocalSend Switch 充当的角色就有点类似于 BT 下载中的 Tracker 服务器了,但同时也会帮忙发送单播的注册请求,用于辅助**组播隔离、单播允许**的局域网子网之间的 LocalSend 客户端互相发现。 ### 2.1. 工作原理 <img src="https://serv.xbottle.top/i/https://raw.githubusercontent.com/SomeBottle/localsend-switch/refs/heads/main/pics/switch_strategy_illustration.drawio.png" alt="Switch Strategy Illustration" width="900px" /> > Figure 3: LocalSend Switch 的工作原理示意图。实线表示的是单播分组的传播路径,虚线表示的是 TCP 逻辑连接;虚线上的箭头对应数据在逻辑上的传播方向。LocalSend 客户端和 Switch 进程的旁边标记了连接端口,只有 VLAN 1 中的 Switch 进程监听了服务端口 `7761`,其余两个 Switch 进程的均为 OS 分配的临时端口;LocalSend 客户端默认服务端口是 `53317`。 Fig.3 为 LocalSend Switch 的工作原理示意图,展示了单次的客户端信息传播以及注册请求代发的过程。图中,首先 `10.84.0.0/15` 网段中 `10.84.123.223` 这台主机上的 LocalSend 客户端发送了组播包,通告自己的存在,被同一台机器上的 LocalSend Switch 捕获到,Switch 进程随后将该通告信息通过单播发送 (图中标记为 `CLIENT ANNOUNCE`,传播路径为蓝色) 给它所连接的所有 Switch 节点 (图中只有 `192.168.232.47:7761` 这一个)。 * 发送的数据中封装了 LocalSend **客户端的 IP 和端口**,无论被转发多少次,这部分数据都不会变,指向**最初发出**这条通告信息的 LocalSend 客户端。 `47` 主机上 Switch 节点接收到通告的客户端信息后,会将该信息转发至它所连接的**其他** Switch 节点(图中只有 `10.94.23.114:52341`),图中标记为 `FORWARD ANNOUNCE`,传播路径为紫色。因为这台主机上没有 LocalSend 客户端,所以不会有注册请求的代发操作。 `114` 主机上的 Switch 节点接收到通告信息后,会将该信息发送给它所连接的其他所有 Switch 节点(图中没有其他节点了);因为这台主机上有 LocalSend 客户端,所以 Switch 节点随后会向通告信息中携带的 LocalSend 客户端地址 (图中为 `10.84.123.223:53317` ) 发送 HTTP(S) 注册请求(图中标记为 `REGISTER CLIENT`,传播路径为棕色),告知对方本地客户端的 IP 和地址 (图中为 `10.94.23.114:53317`),完成注册请求的代发操作。注意这个注册请求是直接由 Switch 发送给 LocalSend 客户端的。 实际上每个 Switch 节点都有这样的转发功能,甚至可以在逻辑上串联或者组成树形、星型、网状、混合等拓扑结构。 ## 3. 更进一步 解决了 LocalSend 互相发现的问题后,我又考虑并解决了以下几个问题: 1. 传输安全性问题。 2. 交换信息的环路问题。 3. 自启动问题。 > 注:这节的 "Switch" 均指 LocalSend Switch 工具。 ### 3.0. 传输安全性问题 Switch 节点间的数据传输在 TCP 连接上进行,默认情况下是**明文**的,其中主要是 LocalSend 客户端的主机的地址、设备型号等信息。 尽管在校园网这种较为可信的局域网中不用担心遭到中间人攻击,而且传输的数据本身也没有那么敏感,但如果中间有的 Switch 节点在外网上,就还是有一定风险的,如中间人可以伪造 LocalSend 客户端信息,诱导其他 Switch 节点向恶意构造的内网客户端地址发送注册请求,从而造成拒绝服务攻击 (DoS)。 为此咱考虑过 **TLS** 加密传输,但是考虑到配置和证书管理的复杂性,最终选择了**预共享密钥** (PSK) 方式来对传输的数据进行简单的对称加密,即持有相同密钥的 Switch 节点才能互相通信,传输的是密文。 * 另外为了防止接收到恶意构造的 LocalSend 客户端信息,限制每个 Switch 节点**仅可向私有 IP 地址发送** HTTP(S) 注册请求,避免主机被利用向公网地址发送请求。 * 这个工具的使用场景实际上挺简单的,因此这个程度的安全性应该已经足够了 (* ̄0 ̄)ノ。 ### 3.1. 交换信息的环路问题 本科学计算机网络时,在网络层这一块就听老师讲过环路问题。而本工具如果不加以限制也会导致同一条 LocalSend 客户端信息被疯狂重复转发,浪费带宽和计算资源。 为了解决这点咱采用了两个措施: 1. **经典的 TTL 机制**。给每条消息都设置一个 TTL 字段,每经过一个 Switch 节点 TTL 则减 `1`,当其减到 `0` 时该消息就不再被转发。 2. **唯一 ID 缓存机制**。每条信息都有一个唯一 ID,由 Switch 节点的*临时随机标识*以及*消息的递增编号*组成。每个 Switch 节点都会避免重复把相同 ID 的客户端信息重复加入转发缓冲区,也就不会重复转发已经转发过的 LocalSend 客户端信息。 * 这个唯一 ID 缓存是有过期时间的,咱默认设置为了 `5` 分钟,以防止内存无限增长。 * *临时随机标识*是在 Switch 启动时生成的,重启后会改变,因此重启后的 Switch 节点会被认为是一个新的节点。 * **唯一 ID 机制**还能一定程度上防止重放攻击。 ### 3.2. 自启动问题 如果每次使用 LocalSend Switch 都需要手动启动的话,多少还是有些麻烦,因此咱特地还写了开机 (登录后) 自启的相关配置模块,目前支持了 Windows 和 Linux (带桌面)。 * Windows 下是参考了 LocalSend 的自启动实现,往注册表里写入了开机自启项。 * Linux 下则是依照 FreeDesktop 的 [Desktop Entry](https://specifications.freedesktop.org/desktop-entry/latest/recognized-keys.html) 规范,写入了 `~/.config/autostart/` 目录下。 * MacOS...咳咳,咱还没苹果电脑呢,暂时没法测试和实现 Or2... ## 4. 项目地址 & 总结 ✨ 项目地址: https://github.com/SomeBottle/localsend-switch 一顿折腾下来,咱又花了好多时间写了这样一个使用场景其实挺小众的工具...不过总的来说还是挺有趣的,一方面熟悉了 Go 语言程序的基本思想和编写规范,另一方面也复习复习了一下计算机网络和基本的网络编程知识(像是手动去搭个 TCP 服务,实现简单的连接管理等等)。 * 复习归复习,个人学艺不精,难免有疏漏之处,请各位多指教! 动手去解决些实际问题,总归是个不错的学习途径呢 (๑•̀ㅂ•́)و✧ 转眼也 2026 了,祝各位新年快乐,事业顺利!咱们下一篇文章再会~ (づ ̄ ³ ̄)づ ![drink_coffee-2026-01-17](https://serv.xbottle.top/i/https://raw.githubusercontent.com/cat-note/bottleassets/main/img/drink_coffee-2026-01-17.gif) <script>/* bblock.s(); */</script> {(PostContentEnd)} {(PostTag)}学习,小记,咸鱼技术,计算机网络{(PostTagEnd)} {(PostID)}307{(PostIDEnd)} {(PostCover)}none{(PostCoverEnd)} {(PubTime)}1768668166965{(PubTimeEnd)} {(EditTime)}1768668166965{(EditTimeEnd)}