IOS-NetWorkExtension
NetworkExtension是苹果提供的用于配置 VPN 和定制、扩展核心网络功能的框架。NE 框架提供了可用于定制、扩展 iOS 和 MacOS 系统的核心网络功能的 API。
Network Extension 最早出现在 iOS 8,不过那个版本不支持虚拟网卡,只能简单调用 iOS 系统自带的 IPSec 和 IKEv2 协议的 VPN。在 iOS 9 中,开发者可以用 NETunnelProvider 扩展核心网络层,从而实现非标准化的私密VPN技术。最重要的两个类是 NETunnelProviderManager
和 NEPacketTunnelProvider
。
- 新建项目
- 建立普通 Swift 项目 Dsocket.
- Network Extension 无法在模拟器上调试。同时,你得有开发者账号,用来申请相关 Capabilities.
- 新建 PacketTunnel
- 新建 Target,选择 Network Extension。
然后选择 Provider Type 为 PacketTunnel。
如果 containing app 要与 extension 共享数据,则必须要开启 App Groups。
Personal VPN 和 Network Extensions(App Proxy、Content Filter、Packet Tunnel)也当然要开启。
NETunnelProviderManager
它和 vpn 设置是一一对应关系。如果 App 有两个 vpn 设置,我们通过代码就能得到两个 NETunnelProviderManager
实例。我们要通过代码,对 NETunnelProviderManager
做四种操作。
- 创建 vpn 配置
fileprivate func createProviderManager() -> NETunnelProviderManager { let manager = NETunnelProviderManager() manager.protocolConfiguration = NETunnelProviderProtocol() return manager }
- 保存 vpn 配置
这段代码执行时会请求用户的授权,允许之后会添加一份 vpn 的配置manager.saveToPreferences { if let error = $0 { complete(nil, error) } else { manager.loadFromPreferences(completionHandler: { (error) -> Void in if let error = error { complete(nil, error) } else { complete(manager, nil) } }) } }
- 开启和关闭 vpn
```swift
fileprivate func startVPNWithOptions(_ options: [String : NSObject]?, complete: ((NETunnelProviderManager?, Error?) -> Void)? = nil) {
// Load provider
loadAndCreateProviderManager { (manager, error) -> Void in
} }if let error = error { complete?(nil, error) } else { guard let manager = manager else { complete?(nil, ManagerError.invalidProvider) return } if manager.connection.status == .disconnected || manager.connection.status == .invalid { do { try manager.connection.startVPNTunnel(options: options) self.addVPNStatusObserver() complete?(manager, nil) }catch { complete?(nil, error) } } else { self.addVPNStatusObserver() complete?(manager, nil) } }
public func stopVPN() { // Stop provider loadProviderManager { guard let manager = $0 else { return } manager.connection.stopVPNTunnel() } }
4. 监听并更新 vpn 状态
```swift
/// 添加vpn的状态的监听
func addVPNStatusObserver() {
guard !observerDidAdd else {
return
}
loadProviderManager {
if let manager = $0 {
self.observerDidAdd = true
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: manager.connection, queue: OperationQueue.main, using: { [unowned self] (notification) -> Void in
self.updateVPNStatus(manager)
})
}
}
}
/// 更新vpn的连接状态
///
/// - Parameter manager: NEVPNManager
func updateVPNStatus(_ manager: NEVPNManager) {
switch manager.connection.status {
case .connected:
self.vpnStatus = .on
case .connecting, .reasserting:
self.vpnStatus = .connecting
case .disconnecting:
self.vpnStatus = .disconnecting
case .disconnected, .invalid:
self.vpnStatus = .off
}
}
- 数据传递
我们需要在主程序中传递类似账号、密码、端口、加密方式等参数给我们的VPN组件。
- 主程序写入
let conf = ["port":1000,"method":"AES-256-CFB","password":"hello"] let providerProtocol = manager.protocolConfiguration as! NETunnelProviderProtocol providerProtocol.providerConfiguration = conf manager.protocolConfiguration = orignConf
- target读取 ```swift public var protocolConfiguration: NEVPNProtocol { get }
guard let conf = (protocolConfiguration as! NETunnelProviderProtocol).providerConfiguration else{ NSLog("[ERROR] No ProtocolConfiguration Found") exit(EXIT_FAILURE) } let address = conf["address"] as! String let port = conf["port"] as! Int
## NEPacketTunnelProvider
真正的 vpn 核心代码。项目中 ``PacketTunnelProvider`` 是其子类,并且以下两个方法必须实现
```swift
@available(iOS 9.0, *)
open func startTunnel(options: [String : NSObject]? = nil, completionHandler: @escaping (Error?) -> Swift.Void)
@available(iOS 9.0, *)
open func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Swift.Void)
当 App 里的 NETunnelProviderManager
对象调用 startVPNWithOptions
时,控制流程就跳到 Extension 里的 startTunnel
方法。
startTunnel
有两个参数:options
是个字典,从 App 里传过来,具体有啥信息完全由开发者自己定义。completionHandler
是个闭包回调,由系统提供。我们可以将其保存到某个变量,待 vpn 启动完成时主动调用。
stopTunnel
也有两个参数:reason 表示 vpn 被关闭的理由。iOS 预先定义了一组 NEProviderStopReason
常数,但实际应用时,reason
基本不用。completionHandler
的用法同 startTunnel
第二个参数一样,不再重复。
在已经完成 PacketTunnel 的代码情况下:
- 构建并运行应用。
- 停止运行。
- Xcode 菜单中
Debug
->attach to process by PID or name
,填入PacketTunnel
,然后Attach
。
- 在手机上运行(不要通过 Xcode)应用,点击连接的时候,进入了断点。
相关文章
- 硅谷互联网公司的开发流程
开发流程包括这么几个阶段: OKR 的设立; 主项目及其子项目的确立; 每个子项目的生命周期; 主项目的生命周期; 收尾、维护、复盘。 第一点,OKR 的设立 所有项目的起始,都应该从 Ro
- RESTful-表述性状态转移风格
REST英文全拼:Representational State Transfer 面向资源编程 资源指的就是一类数据 产品表->就是产品资源 最重要的是如何表示一个资源 地址即
- 稳定性思考
产品功能线 0-1: 当系统从无到有的时候,首要考虑的是研发效率,功能快速迭代,满足快速增长的业务需求 1-10 系统已经搭建起来,此时考虑的是系统的稳定性。 可用性:1.隔离:区分出核心和非核心功能
- Supervisor守护队列发邮件
安装 CentOS: yum -y install supervisor Debien/Ubuntu适用:apt-get install supervisor 配置 修改主配置文件:vim /et
- 安装libsodium,让服务器支持chacha20等加密方式
用chacha20加密方式需要安装libsodium 注意:libsodium从1.0.15开始就废弃了aes-128-ctr yum install wget m2crypto git libsod
随机推荐
- 硅谷互联网公司的开发流程
开发流程包括这么几个阶段: OKR 的设立; 主项目及其子项目的确立; 每个子项目的生命周期; 主项目的生命周期; 收尾、维护、复盘。 第一点,OKR 的设立 所有项目的起始,都应该从 Ro
- RESTful-表述性状态转移风格
REST英文全拼:Representational State Transfer 面向资源编程 资源指的就是一类数据 产品表->就是产品资源 最重要的是如何表示一个资源 地址即
- 稳定性思考
产品功能线 0-1: 当系统从无到有的时候,首要考虑的是研发效率,功能快速迭代,满足快速增长的业务需求 1-10 系统已经搭建起来,此时考虑的是系统的稳定性。 可用性:1.隔离:区分出核心和非核心功能
- Supervisor守护队列发邮件
安装 CentOS: yum -y install supervisor Debien/Ubuntu适用:apt-get install supervisor 配置 修改主配置文件:vim /et
- 安装libsodium,让服务器支持chacha20等加密方式
用chacha20加密方式需要安装libsodium 注意:libsodium从1.0.15开始就废弃了aes-128-ctr yum install wget m2crypto git libsod