ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/9/27 6:12:05 拓冰建站 浏览量
异地组网客户端开发实录: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 foundCentOS 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.3HTTPS 要用、根证书。这几个我是挨个 ls 确认过的。第二个坑更隐蔽。我给容器加了--restart unless-stopped它崩了会自动拉起。启动脚本检查状态那一瞬间容器恰好是 Running于是脚本报了已启动可日志里正在刷Failed to create CoreCLR。容器健康不能只看一眼状态。后来改成看三样日志里有没有致命错误、RestartCount是不是 0、日志里能不能看到已接入这类成功标记。dockerinspect-f{{.State.Running}} {{.RestartCount}}aipai第三个是老版 Docker 拦住了 .NET。CentOS 7 的源里是 Docker 1.132017 年的版本它默认的 seccomp 策略会拦掉 .NET 运行时需要的系统调用报出来的还是Failed to create CoreCLR, HRESULT: 0x80070008。这个错误码字面意思是内存不足实际是 syscall 被拦了。老内核上 .NET 的 W^X 也起不来一起关掉dockerrun... --security-optseccompunconfined\-eDOTNET_EnableWriteXorExecute0-eDOTNET_gcServer0\-eDOTNET_GCHeapHardLimitHex0x14000000...还有一点CentOS 7 的官方源 2024 年就下线了yum install会卡在失效的镜像上很久得先切到 vault 归档源。这个我在安装脚本里自动处理了不处理的话用户看到的就是命令敲下去没反应。五、几个现在回看还算合理的取舍组网内所有端口互通这是产品要的效果开服不用做端口映射。代价是用户协议里必须写清楚不能拿来对外暴露 80/443不能建站不能转租通道。技术上能做到跟使用边界是两件事能写在协议里就别等出事再补。中继一直留着但我没把它当卖点讲。对称型 NAT 打不通的时候总得有条退路不然用户直接判定软件是坏的。它限速、不承诺可用性因为它就是兜底不是线路。收费方式也从卖注册邀请码改成了按月收技术服务费。邀请码那套用户理解不了自己到底买了什么。现在写的是账号与设备管理、打洞调度、控制台、售后口径清楚也避开了卖流量这种怎么解释都别扭的说法。最后三端源码都在 GitHub 上https://github.com/wenzaiclub/aipai-link-clientWindows 是 WPFLinux 是命令行加一个网页面板Android 用 VpnService 加 JNI。服务端账号、调度、锚点、计费、后台没开源。要自己编译的话 README 里写了怎么拉 EasyTier 引擎Android 的原生库也提交在仓库里不用再交叉编译。哪块想细看比如锚点怎么按房间拉起独立进程或者客户端怎么按延迟选节点评论里说一声我另外写。