设置完协议后,接下来要做的是分配协议给VPN管理者:
  [manager setProtocol:p];
  IPSec和IKEv2协议都有一个特性叫做按需(On-demand)。这个特性能够在用户尝试连接网络的时候自动连接。如果你不设置,那么当设备进入睡眠状态时,建立起来的VPN连接会自动断开来达到省电的目的。为了避免这个问题,我们需要做相应的设置。首先,你需要做将onDemandEnabled设为YES:
  [[NEVPNManager sharedManager] setOnDemandEnabled:YES];
  除此之外,你还需要告诉操作系统何时你希望on-demand是有用的。因此,你需要在配置中添加一些策略。这些策略叫做“按需策略”。按需策略是告诉操作系统VPN建立的是按需连接的所有性质的集合。onDemandRules属性接收策略数组,因而,你能给VPN的配置设置多种策略。比如,你可以设置一种策略来告诉操作系统当用户想要打开Apple.com时建立VPN连接,否则,VPN连接将不会建立。有一件事是你可能想要去做的,那是当应用程序建立网络连接的时候激活VPN连接。因此,所有的iOS网络通信将会通过你的VPN服务器进行转换。为了达到这样的效果,你需要使用到NEOnDemandRuleConnect类。在NetworkExtension框架中,苹果提供了一些有用的按需策略的模板。NEOnDemandRuleConnect类是其中的一个模板。它会告诉操作系统当iOS需要连接到网络时建立VPN连接,因而,只要用户需要用到网络会连接到你的VPN服务器。以下是使用NEOnDemandRuleConnect模板的例子:
  [[NEVPNManager sharedManager] setOnDemandEnabled:YES];
  NSMutableArray *rules = [[NSMutableArray alloc] init];
  NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
  [rules addObject:connectRule];
  [[NEVPNManager sharedManager] setOnDemandRules:array];
  后一个需要去设置的是VPN偏好信息的描述信息:
  [manager setLocalizedDescription:@"[You VPN configuration name]"];
  设置完后还没有结束,因为我们还没有将其保存。为了能保存配置信息需要调用aveToPreferencesWithCompletionHandler: method::
  [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
  if(error) {
  NSLog(@"Save error: %@", error);
  }
  else {
  NSLog(@"Saved!");
  }
  }];
  连接VPN connection
  设置完偏好信息后,可以去连接它了。NEVPNManager有一个属性叫做connection。这个属性是NEVPNConnection类。它保存了负责VPN连接的信息。为了能够与VPN服务器相连接,需要调用NEVPNConnection类的startVPNTunnelAndReturnError:方法:
  - (IBAction)buttonPressed:(id)sender {
  NSError *startError;
  [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
  if(startError) {
  NSLog(@"Start error: %@", startError.localizedDescription);
  } else {
  NSLog(@"Connection established!");
  }
  }
  在你的设备上运行程序你会看到当按钮被按下时新的连接会被创建然后与服务器连接。并且,你也可以通过调用stopVPNTunnel方法断开与VPN服务器的连接。
  以下代码来自gist:
- (void)viewDidLoad
{
[super viewDidLoad];
// init VPN manager
self.vpnManager = [NEVPNManager sharedManager];
// load config from perference
[_vpnManager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(@"Load config failed [%@]", error.localizedDescription);
return;
}
NEVPNProtocolIPSec *p = _vpnManager.protocol;
if (p) {
// Protocol exists.
// If you don't want to edit it, just return here.
} else {
// create a new one.
p = [[NEVPNProtocolIPSec alloc] init];
}
// config IPSec protocol
p.username = @"[Your username]";
p.serverAddress = @"[Your server address]";;
// Get password persistent reference from keychain
// If password doesn't exist in keychain, should create it beforehand.
// [self createKeychainValue:@"your_password" forIdentifier:@"VPN_PASSWORD"];
p.passwordReference = [self searchKeychainCopyMatching:@"VPN_PASSWORD"];
// PSK
p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
// [self createKeychainValue:@"your_psk" forIdentifier:@"PSK"];
p.sharedSecretReference = [self searchKeychainCopyMatching:@"PSK"];
/*
// certificate
p.identityData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
p.identityDataPassword = @"[Your certificate import password]";
*/
p.localIdentifier = @"[VPN local identifier]";
p.remoteIdentifier = @"[VPN remote identifier]";
p.useExtendedAuthentication = YES;
p.disconnectOnSleep = NO;
_vpnManager.protocol = p;
_vpnManager.localizedDescription = @"IPSec Demo";
[_vpnManager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(@"Save config failed [%@]", error.localizedDescription);
}
}];
}];
}
- (IBAction)startVPNConnection:(id)sender {
//[[VodManager sharedManager] installVPNProfile];
NSError *startError;
[_vpnManager.connection startVPNTunnelAndReturnError:&startError];
if (startError) {
NSLog(@"Start VPN failed: [%@]", startError.localizedDescription);
}
}
#pragma mark - KeyChain
static NSString * const serviceName = @"im.zorro.ipsec_demo.vpn_config";
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
[searchDictionary setObject:serviceName forKey:(__bridge id)kSecAttrService];
return searchDictionary;
}
- (NSData *)searchKeychainCopyMatching:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
// Add search attributes
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
// Add search return types
// Must be persistent ref !!!!
[searchDictionary setObject:@YES forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &result);
return (__bridge_transfer NSData *)result;
}
- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dictionary);
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(__bridge id)kSecValueData];
status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
if (status == errSecSuccess) {
return YES;
}
return NO;
}
  以上。