brew install go
open ~/.bash_profile
export PATH=”$PATH:$(go env GOPATH)/bin”
先确保工具链完整
go install github.com/sagernet/gomobile/cmd/gomobile@v0.1.4 go install github.com/sagernet/gomobile/cmd/gobind@v0.1.4
gomobile init -v # 关键:-v 会打印详细日志
官方其实有现成的入口,不需要手写 gomobile
# 在源码根目录执行
make lib_install # 安装 gomobile 专用 fork
make lib_ios # 官方脚本会自动修补导出
xcframework successfully written out to: /Users/m/Desktop/sing-box-1.11.0/Libbox.xcframework
✅ 它到底是什么?
一个 Xcode 支持的二进制框架包
内部包含 ios-arm64(真机) ios-arm64_x86_64-simulator(模拟器)
你在 NetworkExtension 里 import Libbox 就能调用 sing-box 的 C/Go API 拖进 Xcode ▸ Embed & Sign 即可使用
如果之前创建过一个相同的项目,然后又删了重新创建这个相同id的项目就会爆错。
同一个 Bundle ID(com.your.vlessdemo 及其子 ID)之前已经注册过,被你手动删掉后,Apple 后台其实还占着坑,导致新的工程下载不到有效的 Provisioning Profile,于是签名时报 “code object is not signed at all”。
彻底清掉旧 ID(要等 24 h)
登录 developer.apple.com → Certificates, IDs & Profiles → Identifiers
在右上角 搜索框 输入 com.your.vlessdemo
如果还能看到旧 ID → 点击 → Delete
24 小时后才能重新注册同名 ID,不推荐。
✅ 1️⃣ 系统 → PacketTunnelProvider
触发点:用户在 iOS 设置里点击“启动”。
iOS 系统会自动调用:
PacketTunnelProvider.startTunnel(options:)
✅ 2️⃣ PacketTunnelProvider → LibboxNewService
在 startTunnel 里,你把 4 样东西交给 LibboxNewService:
// configStr : 剧本(json)
// platform : 舞台管理员(PlatformInterface)
// &err : 错误指针
let svc = LibboxNewService(configStr, platform, &err)
此时 Libbox(sing-box 引擎) 内部会保存这个 platform 对象,但不会立刻用到它。
✅ 3️⃣ Libbox 内部 → PlatformInterface.openTun
sing-box 启动后,需要建立 TUN 虚拟网卡,于是它主动调用你实现的:
PlatformInterface.openTun(_ options:ret0_:)
| 参数 | 含义 |
|---|---|
options |
sing-box 想要的 IP、路由、MTU 等需求 |
ret0_ |
一个返回指针,你必须把 TUN 的文件描述符写进去 |
✅ 4️⃣ openTun 内部 → iOS 系统
在 openTun 里,你用 iOS 系统 API 完成三件事:
创建网络设置
let settings = NEPacketTunnelNetworkSettings(...)
settings.mtu = ...
settings.dnsSettings = ...
settings.ipv4Settings = ...
settings.ipv4Settings?.includedRoutes = ...
把设置写入系统
try runBlocking { try await tunnel.setTunnelNetworkSettings(settings) }
把 TUN 文件描述符返回给 sing-box
let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 ?? LibboxGetTunnelFileDescriptor()
fd.pointee = tunFd // ← 写回给 sing-box
✅ 5️⃣ sing-box 收到文件描述符后
sing-box 拿到 tunFd,就可以:
从 TUN 读 IP 数据包(用户 App 发出的流量)
做代理/路由处理
把结果再写回 TUN
✅ 6️⃣ 用户关闭 VPN 时
系统调用:
PacketTunnelProvider.stopTunnel(with:completionHandler:)
你在里面:
try? service?.close() // 通知 sing-box 关机
completionHandler() // 告诉系统“我已清理完毕”
✅ 完整“谁调谁”链路图
iOS 系统
│ 1️⃣ startTunnel
▼
PacketTunnelProvider
│ 2️⃣ LibboxNewService(config, platform)
▼
sing-box 引擎
│ 3️⃣ platform.openTun(options, &fd)
▼
PlatformInterface
│ 4️⃣ setTunnelNetworkSettings(settings)
▼
iOS 网络栈
│ 5️⃣ 返回 tunFd 给 sing-box
▼
sing-box 开始转发流量
架构详解 – 新手小白版
📋 文件职责分工
1️⃣ PacketTunnelProvider.swift – 总经理
class PacketTunnelProvider: NEPacketTunnelProvider {
// "总指挥"
}
主要职责:
🎯 启动连接:startTunnel() – 接收iOS系统的启动命令
📁 准备工作环境:创建工作目录、缓存目录
⚙️ 初始化sing-box:调用LibboxSetup()设置Go运行环境
📖 读取配置:获取VLESS服务器配置、分流规则
🔧 创建服务:调用LibboxNewService()创建代理服务
🛑 停止:stopTunnel() – 清理资源
代码流程:
startTunnel() {
1. 创建工作目录
2. 调用 LibboxSetup()
3. 读取配置文件
4. 创建 PlatformInterface
5. 调用 LibboxNewService(配置, PlatformInterface)
6. 启动服务
}
2️⃣ PlatformInterface.swift – 技术顾问
class PlatformInterface: NSObject, LibboxPlatformInterfaceProtocol {
// 这是iOS系统和Go代码的"翻译官"
}
主要职责:
🌐 网络设置:openTun() – 设置TUN接口、DNS、路由
🔌 系统桥梁:让Go代码能调用iOS系统功能
📝 日志输出:writeLog() – 转发sing-box日志到iOS
🚫 功能适配:禁用iOS不支持的功能
核心方法:
func openTun() {
1. 设置DNS服务器 (8.8.8.8)
2. 配置IPv4地址和路由
3. 应用网络设置到iOS系统
4. 获取TUN接口文件描述符
5. 返回给Go代码使用
}
💡 关键设计思想
🎯 为什么需要PlatformInterface?
Go语言限制:Go代码无法直接调用iOS系统API
沙盒隔离:NetworkExtension有严格的权限限制
系统集成:需要设置DNS、路由、TUN接口等iOS特有功能
🔄 为什么分成两个文件?
职责分离:总经理管大局,技术顾问管细节
代码复用:PlatformInterface可以给其他项目复用
维护方便:网络设置相关代码集中在一起
🎉 总结
这个架构巧妙地解决了Go语言sing-box与iOS NetworkExtension的兼容性问题:
PacketTunnelProvider:iOS的标准入口,负责整体流程控制
PlatformInterface:iOS与Go的桥梁,提供系统级功能
Libbox:强大的网络代理引擎,处理实际的流量转发
通过这种设计,成功实现了国内IP走代理,国外IP直连的智能分流!🚀


















暂无评论内容