news 2026/9/27 6:11:52

异地组网客户端开发实录:P2P 打洞、在线判定、老系统兼容踩过的坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
异地组网客户端开发实录:P2P 打洞、在线判定、老系统兼容踩过的坑

摘要:把我手上的几台机器连成一张虚拟局域网,Windows、Linux、Android 三端,引擎用 EasyTier,账号、调度、锚点、后台都自己搭。这里只记三件事:为什么有台设备死活走不了 P2P,在线状态为什么不能指望客户端心跳,CentOS 7 上跑不起来怎么办。仓库在文末。


先说这个东西是干什么的

需求很土:我自己名下几台机器,家里台式机、公司电脑、一台 NAS,加两部手机,想把它们连成一张虚拟局域网。能互相 ping 通,能联机开服,能共享文件和打印机就够了。

不好办的地方有三处:

  • 开服那台没有公网 IP,我也不想为它做端口映射
  • 虚拟 IP 必须固定,机器换个网络不能变地址
  • 手机得能进

现在做出来的形态是:客户端登录,填一个 8 位适配码,客户端在本机拉起 EasyTier 引擎,建虚拟网卡和路由。账号、设备分配、锚点调度在服务端。

打洞和中继 EasyTier 自己都做了,我写的其实是它外面那一圈。按踩坑的先后顺序讲。

一、走不走 P2P,先看 NAT 类型

一开始我想的是两边都在国内、延迟又低,直连应该没问题。结果有台设备一直挂在中继上,速度只有几百 KB/s。查下来,它是对称型 NAT。

EasyTier 会把对端的 NAT 类型和当前链路一起报出来,用自带的 CLI 就能看:

easytier-cli-p127.0.0.1:15888-ojson peer

输出里每条对端长这样(节选):

{"ipv4":"10.0.0.5","hostname":"pc-a","cost":"p2p","lat_ms":"5.45","loss_rate":"0.0%","tunnel_proto":"tcp,udp","nat_type":"SymmetricEasyInc"}

cost是p2p就是直连,出现relay就是走中继了。nat_type常见的几种:

  • PortRestricted/Restricted:锥形 NAT,一般打得通
  • Symmetric/SymmetricEasyInc:端口每次都换,打洞基本没戏

知道这个以后我改了两处。客户端界面上把每个对端标出来是直连还是中继,省得用户自己猜;另外把"能不能直连"和"节点延迟"拆成两个指标。创建组网时我们会并发探测各调度节点的 TCP 延迟,挑最近的那个当锚点,但锚点近不代表这两台客户端之间打得到洞。这两个指标我曾经混在一起看,结论是错的。

中继现在在锚点侧限了速。它是兜底,不是主路径。P2P 的数据不经过服务器,不受这个限制。

二、在线状态别指望客户端

一开始的判定方式很直接:客户端连上以后定时给服务端发心跳,服务端写last_seen,查询时看这个时间有没有超出窗口,我们定的是 90 秒。

Windows 和 Linux 都正常。安卓加进组网之后,界面上一直是"离线"。

查了半天,是它压根没发心跳。安卓用系统VpnService建 tun,再把 fd 交给 JNI 里的 EasyTier,跟桌面端是两条完全不同的路,写的时候漏了这一段。服务端记录里也看得出来,那台机器的last_seen是 NULL。

补心跳当然能解决,但我想的是另一件事:锚点本来就知道谁连着。所有客户端都得连锚点,信令至少要从那儿过,锚点上的对端列表比客户端自己报的准。让每个端各实现一遍心跳还得先定协议,不如直接问锚点:

# 锚点的 RPC 端口按房间号算,形如 16000 + (房间号 % 2000)easytier-cli-p127.0.0.1:16021-ojson peer

拿到对端 IP 列表,跟数据库里该组网的成员按虚拟 IP 对一遍,命中的就刷last_seen。这段逻辑放在锚点的管理脚本里,跟着定时任务一分钟跑一次:

UPDATEnetwork_membersSETlast_seen=?WHEREnetwork_id=?ANDSUBSTRING_INDEX(v4_addr,'/',1)IN(?,?,...)

这样客户端不用配合。安卓那个没心跳的版本没更新,Linux 的老版本也没更新,都自动显示在线了。设备真断了之后,对端会从锚点列表里消失,last_seen不再刷新,过窗口自然变离线。安卓那个坑我到现在也没回头去补心跳,因为用不上了。

三、固定虚拟 IP 交给服务端分

需求是"不删组网,IP 就不变"。实现不难,重点是别让客户端自己决定。

设备第一次加入某个组网时,服务端在成员表里给它分配一个虚拟地址写进库,顺便记下设备标识、网段和网关。之后这台机器的内网 IP 怎么变、重连多少次、客户端重装(只要设备标识还在),拿到的都是同一个地址。

客户端那边就简单了:登录、拉配置、从配置里读自己的虚拟地址、拿这个地址起引擎。不猜,也不看本地网段。

顺带说个容易搞错的点。虚拟机里跑游戏服务端,监听地址填0.0.0.0有时候收不到组网网卡上过来的连接,得显式写组网虚拟 IP。这个问题我查了很久,最后确认是虚拟机的网络栈把0.0.0.0的绑定限制在某张网卡上了。不是组网的问题,但确实踩过。

四、老系统上跑不起来,这块最费时间

有台 CentOS 7 的机器要当组网节点。客户端装完,命令行直接报错:

aipai: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by aipai) aipai: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.22' not found

CentOS 7 是 glibc 2.17、GLIBCXX 3.4.19,而客户端是 .NET 自包含程序,底线是 glibc 2.27 加 GLIBCXX 3.4.22。这不是装个依赖就能补的事,glibc 没法单独升级,硬换会把系统弄坏。

剩下的路只有容器。容器里的用户空间是新的,虚拟网卡还是建在宿主机上,效果跟直装一样:

docker run -d --name aipai --restart unless-stopped \ --network host --cap-add NET_ADMIN --cap-add NET_RAW \ --device /dev/net/tun \ -e AIPAI_USER=账号 -e AIPAI_PASSWORD=密码 -e AIPAI_NETWORK=适配码 \ -v /opt/aipai-docker/app:/app -v /opt/aipai-docker/data:/root/.config/aipai \ -w /app 基础镜像 /app/aipai connect

真做起来又踩了三个坑。

第一个是镜像拉不动。国内访问 Docker Hub 基本没戏,配了加速器也可能一起挂。最后的办法是把一个精简过的 Ubuntu 镜像放到自己服务器上,官方镜像拉不到就docker import导进去:

dockerimportaipai-base.tar.xz aipai-base:jammy

原镜像解包 684MB,删掉包索引、Python、vim、文档和本地化之后剩 211MB,压成 xz 是 50MB。容器里跑 .NET 需要的东西一个都不能少:glibc、libstdc++、libssl.so.3(HTTPS 要用)、根证书。这几个我是挨个 ls 确认过的。

第二个坑更隐蔽。我给容器加了--restart unless-stopped,它崩了会自动拉起。启动脚本检查状态那一瞬间,容器恰好是 Running,于是脚本报了"已启动",可日志里正在刷Failed to create CoreCLR。

容器健康不能只看一眼状态。后来改成看三样:日志里有没有致命错误、RestartCount是不是 0、日志里能不能看到"已接入"这类成功标记。

dockerinspect-f'{{.State.Running}} {{.RestartCount}}'aipai

第三个是老版 Docker 拦住了 .NET。CentOS 7 的源里是 Docker 1.13(2017 年的版本),它默认的 seccomp 策略会拦掉 .NET 运行时需要的系统调用,报出来的还是Failed to create CoreCLR, HRESULT: 0x80070008。这个错误码字面意思是内存不足,实际是 syscall 被拦了。老内核上 .NET 的 W^X 也起不来,一起关掉:

dockerrun... --security-optseccomp=unconfined\-eDOTNET_EnableWriteXorExecute=0-eDOTNET_gcServer=0\-eDOTNET_GCHeapHardLimitHex=0x14000000...

还有一点:CentOS 7 的官方源 2024 年就下线了,yum install会卡在失效的镜像上很久,得先切到 vault 归档源。这个我在安装脚本里自动处理了,不处理的话用户看到的就是"命令敲下去没反应"。

五、几个现在回看还算合理的取舍

组网内所有端口互通,这是产品要的效果,开服不用做端口映射。代价是用户协议里必须写清楚:不能拿来对外暴露 80/443,不能建站,不能转租通道。技术上能做到跟使用边界是两件事,能写在协议里就别等出事再补。

中继一直留着,但我没把它当卖点讲。对称型 NAT 打不通的时候总得有条退路,不然用户直接判定软件是坏的。它限速、不承诺可用性,因为它就是兜底,不是线路。

收费方式也从卖注册邀请码改成了按月收技术服务费。邀请码那套,用户理解不了自己到底买了什么。现在写的是账号与设备管理、打洞调度、控制台、售后,口径清楚,也避开了"卖流量"这种怎么解释都别扭的说法。

最后

三端源码都在 GitHub 上:

https://github.com/wenzaiclub/aipai-link-client

Windows 是 WPF,Linux 是命令行加一个网页面板,Android 用 VpnService 加 JNI。服务端(账号、调度、锚点、计费、后台)没开源。要自己编译的话 README 里写了怎么拉 EasyTier 引擎,Android 的原生库也提交在仓库里,不用再交叉编译。

哪块想细看,比如锚点怎么按房间拉起独立进程,或者客户端怎么按延迟选节点,评论里说一声,我另外写。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 6:11:37

企业seo优化方案报价多少钱

3个坑害企业官网SEO的致命漏洞避坑指南 网站做好了没人访问,是不是心在滴血?别急着加广告,先查查你的站是不是中了“隐形毒”。很多老板以为SEO就是堆关键词,其实 网站安全才是SEO的命门 。一个未被修复的SQL注入漏洞,能让Google瞬间把你的站踢出索引。这篇 避坑指南…

作者头像 李华
网站建设 2026/9/27 6:11:27

AI获客: 杭州本地GEO源头厂商公司汇总(9月)

GEO源头厂商,是指围绕生成式引擎优化(GEO)进行底层研发、系统自研与持续服务的厂商,而非只做转包、套壳或单点工具的服务商。其核心工作是帮助企业信息被AI大模型收录、引用与推荐,解决用户向豆包、DeepSeek、千问、文…

作者头像 李华
网站建设 2026/9/27 6:11:20

晶晨S905L3S/L3SB机顶盒安卓9.0通刷固件免拆刷机全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 6:10:54

彩票做网站5大注意事项,避坑指南与报价全解析

彩票做网站5大注意事项,避坑指南与报价全解析 网站做好了没人访问,这是很多做彩票相关站点老板最头疼的问题。明明花了钱,页面也做得挺漂亮,结果后台数据惨淡,流量稀稀拉拉。这时候往往不是技术不行,而是你在前期没搞懂 彩票做网站 背后的合规红线和SEO底层逻辑。…

作者头像 李华
网站建设 2026/9/27 6:10:52

福州关键词seo实战:从备案迷雾到建站报价的避坑指南

福州关键词seo实战:从备案迷雾到建站报价的避坑指南 备案流程一头雾水,是很多福州企业主在启动网站项目时的第一道坎。很多人以为搞定域名和服务器就能直接开干,结果卡在ICP备案的审核环节,不仅耽误了工期,还让手里的建站报价单变得毫无意义。这种因信息不对称导致的焦虑,往往比技术难题更让人头疼。…

作者头像 李华
网站建设 2026/9/27 6:10:36

网站设计原则的历史怎么选,新手避坑指南

网站设计原则的历史怎么选,新手避坑指南 自己不会代码想做网站,别慌,这年头技术门槛已经低到地板了。但很多新手一上来就纠结选哪种建站方式,结果要么花大价钱被外包坑,要么自己折腾半个月还没上线。其实, 怎么选 才是关键。这里有个冷知识:你选定的建站路径,很大程度上决定了你未来三年网站的生死。 根据…

作者头像 李华