MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的NetworkExtension网络扩展

2022-03-127.7k 阅读

一、NetworkExtension 框架概述

在 iOS 和 macOS 开发中,NetworkExtension 框架提供了强大的网络扩展功能,允许开发者在应用内对网络连接进行定制化管理,这对于开发具有特殊网络需求的应用,如 VPN 客户端、网络代理等,尤为重要。Objective - C 作为苹果生态系统早期的主要编程语言,能够很好地利用 NetworkExtension 框架的特性来实现复杂的网络功能。

NetworkExtension 框架提供了多个类来处理不同类型的网络扩展场景,例如 NEVPNManager 用于管理 VPN 配置和连接,NEHotspotConfigurationManager 用于管理 Wi - Fi 热点配置等。这些类为开发者提供了高层次的接口,通过这些接口,开发者可以以一种相对简单的方式与系统网络服务进行交互,而无需深入了解底层网络协议的细节。

二、VPN 相关功能实现

2.1 创建和配置 VPN 连接

在 Objective - C 中,要创建和配置 VPN 连接,我们主要使用 NEVPNManager 类。首先,需要获取 NEVPNManager 的共享实例:

NEVPNManager *vpnManager = [NEVPNManager sharedManager];

接下来,我们可以配置 VPN 的各种参数,比如服务器地址、认证方式等。以配置 IKEv2 VPN 为例:

vpnManager.protocolConfiguration = [[NEVPNProtocolIKEv2 alloc] init];
NEVPNProtocolIKEv2 *ikev2Protocol = (NEVPNProtocolIKEv2 *)vpnManager.protocolConfiguration;
ikev2Protocol.serverAddress = @"vpn.example.com";
ikev2Protocol.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
// 如果使用用户名密码认证
// ikev2Protocol.username = @"your_username";
// ikev2Protocol.passwordReference = (NSData *)CFBridgingRelease(SecKeychainFindGenericPassword(NULL, 0, NULL, 0, NULL, 0, NULL, NULL));

在上述代码中,我们设置了 IKEv2 VPN 的服务器地址,并选择了证书认证方式。如果要使用用户名密码认证,可以按照注释中的代码进行设置。

配置完成后,需要保存配置:

[vpnManager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"保存 VPN 配置失败: %@", error);
    } else {
        NSLog(@"VPN 配置保存成功");
    }
}];

2.2 连接和断开 VPN

一旦配置保存成功,就可以连接 VPN 了。连接 VPN 使用 NEVPNManagerconnect 方法:

[vpnManager connect];

要监控 VPN 的连接状态,可以注册 NSNotificationCenter 来监听 NEVPNStatusDidChange 通知:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnStatusDidChange:) name:NEVPNStatusDidChangeNotification object:nil];
- (void)vpnStatusDidChange:(NSNotification *)notification {
    NEVPNStatus status = [((NEVPNConnection *)notification.object) status];
    switch (status) {
        case NEVPNStatusConnecting:
            NSLog(@"VPN 正在连接...");
            break;
        case NEVPNStatusConnected:
            NSLog(@"VPN 已连接");
            break;
        case NEVPNStatusDisconnecting:
            NSLog(@"VPN 正在断开连接...");
            break;
        case NEVPNStatusDisconnected:
            NSLog(@"VPN 已断开连接");
            break;
        default:
            break;
    }
}

当需要断开 VPN 连接时,调用 NEVPNConnectiondisconnect 方法:

NEVPNConnection *connection = vpnManager.connection;
[connection disconnect];

2.3 处理 VPN 认证挑战

在连接 VPN 过程中,可能会遇到认证挑战,比如需要输入用户名密码或者提供证书。我们可以通过实现 NEVPNConnectionDelegate 协议来处理这些挑战。首先,在类中声明遵循该协议:

@interface YourViewController : UIViewController <NEVPNConnectionDelegate>

然后,设置 NEVPNConnection 的代理:

NEVPNConnection *connection = vpnManager.connection;
connection.delegate = self;

实现代理方法来处理认证挑战:

- (void)vpnConnection:(NEVPNConnection *)vpnConnection willSendIKEv2AuthenticationChallenge:(NSMutableDictionary<NSString *,id> *)challenge completionHandler:(void (^)(NSDictionary<NSString *,id> * _Nullable response))completionHandler {
    // 处理 IKEv2 认证挑战
    if ([challenge[NSURLAuthenticationMethodEAP] isEqualToString:@"EAP - MSCHAP - v2"]) {
        NSMutableDictionary *response = [NSMutableDictionary dictionary];
        response[NSURLAuthenticationMethodEAP] = @"EAP - MSCHAP - v2";
        response[NSURLAuthenticationUsernameKey] = @"your_username";
        response[NSURLAuthenticationPasswordKey] = @"your_password";
        completionHandler(response);
    }
}

在上述代码中,我们处理了 IKEv2 认证中的 EAP - MSCHAP - v2 挑战,提供了用户名和密码。

三、网络代理相关功能实现

3.1 设置 HTTP 代理

在应用内设置 HTTP 代理可以通过 NEProxySettings 类来实现。首先,创建一个 NEProxySettings 对象:

NEProxySettings *proxySettings = [[NEProxySettings alloc] init];
proxySettings.httpEnabled = YES;
proxySettings.httpServer = [[NEProxyServer alloc] initWithAddress:@"proxy.example.com" port:8080];

上述代码启用了 HTTP 代理,并设置了代理服务器地址和端口。如果需要设置 HTTPS 代理,同样可以进行类似的设置:

proxySettings.httpsEnabled = YES;
proxySettings.httpsServer = [[NEProxyServer alloc] initWithAddress:@"proxy.example.com" port:8080];

要将这些代理设置应用到应用的网络请求中,可以通过 NSURLSessionConfiguration 来配置:

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.connectionProxyDictionary = [proxySettings dictionaryRepresentation];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

3.2 自动代理配置(PAC)

自动代理配置(PAC)允许根据 URL 动态选择代理服务器。在 NetworkExtension 中,可以通过设置 NEProxySettingspacScriptURL 属性来实现。首先,准备一个 PAC 脚本文件,并将其放置在服务器上,例如 http://example.com/proxy.pac。然后,设置代理配置:

NEProxySettings *proxySettings = [[NEProxySettings alloc] init];
proxySettings.automaticProxyConfigurationEnabled = YES;
proxySettings.automaticProxyConfigurationURL = [NSURL URLWithString:@"http://example.com/proxy.pac"];

同样,需要将这些设置应用到 NSURLSessionConfiguration 中:

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.connectionProxyDictionary = [proxySettings dictionaryRepresentation];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

四、Wi - Fi 热点配置管理

4.1 创建和保存 Wi - Fi 热点配置

在 Objective - C 中,使用 NEHotspotConfigurationManager 类来管理 Wi - Fi 热点配置。首先,创建一个 NEHotspotConfiguration 对象:

NEHotspotConfiguration *configuration = [[NEHotspotConfiguration alloc] initWithSSID:@"YourWiFiSSID" passphrase:@"YourWiFiPassword" isWEP:NO];

上述代码创建了一个 Wi - Fi 热点配置,指定了 SSID 和密码,并表明不是 WEP 加密方式。然后,保存这个配置:

[NEHotspotConfigurationManager.sharedManager applyConfiguration:configuration completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"保存 Wi - Fi 热点配置失败: %@", error);
    } else {
        NSLog(@"Wi - Fi 热点配置保存成功");
    }
}];

4.2 连接 Wi - Fi 热点

保存配置后,可以尝试连接 Wi - Fi 热点。这需要用户授权,因为连接 Wi - Fi 涉及到系统级操作。可以通过调用 NEHotspotConfigurationManagerapplyConfiguration:completionHandler: 方法来触发连接,系统会弹出授权提示框:

[NEHotspotConfigurationManager.sharedManager applyConfiguration:configuration completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"连接 Wi - Fi 热点失败: %@", error);
    } else {
        NSLog(@"正在尝试连接 Wi - Fi 热点");
    }
}];

4.3 监控 Wi - Fi 连接状态

要监控 Wi - Fi 连接状态,可以注册 NSNotificationCenter 来监听 NEHotspotHelperDidDetectNetwork 通知:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hotspotNetworkDetected:) name:NEHotspotHelperDidDetectNetwork object:nil];
- (void)hotspotNetworkDetected:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NEHotspotNetwork *network = userInfo[NEHotspotHelperNetworkKey];
    if (network.isAutoJoined) {
        NSLog(@"已自动连接到 Wi - Fi 热点: %@", network.SSID);
    }
}

在上述代码中,当检测到 Wi - Fi 网络时,我们检查是否是自动连接的,并打印出 SSID。

五、网络扩展的权限与限制

  1. 权限要求
    • 使用 NetworkExtension 框架中的 VPN 相关功能,应用需要在 Info.plist 文件中声明特定的权限。例如,对于 iOS 应用,需要添加 NEVPNManager 的权限声明:
<key>NEVPNManager</key>
<dict>
    <key>NSRequiresEntitlement</key>
    <true/>
</dict>
- 在 macOS 上,也需要类似的权限声明,具体取决于应用所使用的网络扩展功能类型。

2. 限制 - 系统限制:苹果系统对网络扩展功能有严格的限制,以确保系统的安全性和稳定性。例如,VPN 应用必须通过苹果的审核流程,并且不能进行恶意的网络操作,如未经授权的网络监听等。 - 应用沙盒限制:在 iOS 和 macOS 应用沙盒环境下,网络扩展功能也受到一定限制。例如,应用不能直接访问底层网络设备,所有网络操作都必须通过系统提供的接口进行。这就要求开发者在实现网络扩展功能时,要遵循苹果的开发规范,合理使用 NetworkExtension 框架提供的接口。

六、与其他框架的结合使用

  1. 与 CoreData 的结合 在开发网络扩展应用时,可能需要存储一些网络配置信息,如 VPN 配置、代理设置等。这时可以结合 CoreData 框架来进行数据持久化。例如,将 VPN 配置中的服务器地址、认证方式等信息存储到 CoreData 实体中:
// 获取 CoreData 上下文
NSManagedObjectContext *context = [self managedObjectContext];
// 创建 VPN 配置实体
VPNConfiguration *vpnConfig = [NSEntityDescription insertNewObjectForEntityForName:@"VPNConfiguration" inManagedObjectContext:context];
vpnConfig.serverAddress = @"vpn.example.com";
vpnConfig.authenticationMethod = @"Certificate";
NSError *error = nil;
if (![context save:&error]) {
    NSLog(@"保存 VPN 配置到 CoreData 失败: %@", error);
}
  1. 与 MapKit 的结合 对于一些需要根据地理位置进行网络配置的应用,可以结合 MapKit 框架。例如,当用户处于特定地理位置时,自动切换到相应的 VPN 配置或网络代理。通过获取用户的位置信息:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager requestWhenInUseAuthorization];
[locationManager startUpdatingLocation];
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    CLLocation *location = locations.lastObject;
    // 根据位置信息进行网络配置切换
    if (location.coordinate.latitude > 30.0 && location.coordinate.longitude < 120.0) {
        // 切换到特定的 VPN 配置
        NEVPNManager *vpnManager = [NEVPNManager sharedManager];
        vpnManager.protocolConfiguration = [[NEVPNProtocolIKEv2 alloc] init];
        // 配置其他参数
        // 保存配置等操作
    }
}

在上述代码中,当获取到用户位置信息后,根据经纬度判断是否满足特定条件,如果满足则切换到特定的 VPN 配置。

通过合理结合其他框架,能够进一步增强 NetworkExtension 网络扩展功能的实用性和灵活性,满足更复杂的应用需求。

七、常见问题与解决方法

  1. VPN 连接失败
    • 问题描述:调用 connect 方法后,VPN 无法连接,并且在 NEVPNStatusDidChange 通知中收到 NEVPNStatusDisconnected 状态。
    • 可能原因
      • 配置错误,如服务器地址不正确、认证方式不匹配等。可以仔细检查 VPN 配置参数,确保服务器地址、用户名密码(如果使用)、认证方式等都正确无误。
      • 网络问题,如设备当前网络不可用,或者存在网络限制。可以通过检查设备的网络连接状态,尝试连接其他网络服务来确认网络是否正常。
      • 权限问题,应用可能没有获取到足够的权限来进行 VPN 连接。确保在 Info.plist 文件中正确声明了所需的权限,并且应用已经通过苹果审核(如果是上架应用)。
    • 解决方法
      • 重新检查和修正 VPN 配置,如重新输入服务器地址、确认认证方式等。
      • 检查网络连接,尝试切换网络(如从 Wi - Fi 切换到蜂窝网络),或者检查网络限制设置。
      • 确认权限设置,必要时重新提交应用审核以获取正确的权限。
  2. 代理设置不生效
    • 问题描述:设置了 HTTP 代理或自动代理配置(PAC)后,应用的网络请求仍然没有通过代理服务器。
    • 可能原因
      • 代理设置没有正确应用到 NSURLSessionConfiguration 中。确保在设置 NEProxySettings 后,正确地将其 dictionaryRepresentation 应用到 NSURLSessionConfigurationconnectionProxyDictionary 属性中。
      • 网络请求可能没有使用配置了代理的 NSURLSession。检查应用中发起网络请求的代码,确保使用的是配置了代理的 NSURLSession 实例。
      • 代理服务器问题,如代理服务器不可用、端口被占用等。可以尝试使用其他代理服务器,或者检查代理服务器的运行状态。
    • 解决方法
      • 再次确认代理设置到 NSURLSessionConfiguration 的应用过程,确保代码正确。
      • 检查网络请求代码,修改为使用配置了代理的 NSURLSession
      • 联系代理服务器管理员,确认代理服务器状态,或者尝试更换代理服务器。
  3. Wi - Fi 热点连接失败
    • 问题描述:调用 applyConfiguration:completionHandler: 方法后,无法成功连接到 Wi - Fi 热点。
    • 可能原因
      • 配置错误,如 SSID 或密码不正确。仔细检查 NEHotspotConfiguration 中设置的 SSID 和密码,确保与实际的 Wi - Fi 热点信息一致。
      • 用户未授权,连接 Wi - Fi 热点需要用户授权,如果用户拒绝授权,则无法连接。在调用 applyConfiguration:completionHandler: 方法后,系统会弹出授权提示框,确保用户点击了授权按钮。
      • Wi - Fi 热点问题,如热点信号弱、连接设备数量限制等。可以尝试靠近 Wi - Fi 热点,或者检查热点的连接限制设置。
    • 解决方法
      • 重新检查 Wi - Fi 热点配置,确保 SSID 和密码正确。
      • 如果用户未授权,向用户提示需要授权连接 Wi - Fi 热点,并再次调用 applyConfiguration:completionHandler: 方法。
      • 尝试解决 Wi - Fi 热点自身的问题,如调整热点位置、联系热点管理员解除连接限制等。

通过对这些常见问题的分析和解决,可以帮助开发者在使用 NetworkExtension 框架进行网络扩展开发时,更顺利地实现所需的功能。