Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在iOS 8中以编程方式连接到VPN_Ios_Objective C_Ios8_Vpn - Fatal编程技术网

在iOS 8中以编程方式连接到VPN

在iOS 8中以编程方式连接到VPN,ios,objective-c,ios8,vpn,Ios,Objective C,Ios8,Vpn,自从iOS8测试版发布以来,我在其捆绑包中找到了一个网络扩展框架,它将允许开发人员以编程方式配置并连接到VPN服务器,而无需安装任何配置文件 该框架包含一个名为NEVPNManager的主要类。这个类还有3个主要方法,可以保存、加载或删除VPN首选项。我在viewDidLoad方法中编写了一段代码,如下所示: NEVPNManager *manager = [NEVPNManager sharedManager]; [[NSNotificationCenter defaultCenter] ad

自从iOS8测试版发布以来,我在其捆绑包中找到了一个网络扩展框架,它将允许开发人员以编程方式配置并连接到VPN服务器,而无需安装任何配置文件

该框架包含一个名为NEVPNManager的主要类。这个类还有3个主要方法,可以保存、加载或删除VPN首选项。我在viewDidLoad方法中编写了一段代码,如下所示:

NEVPNManager *manager = [NEVPNManager sharedManager];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnConnectionStatusChanged) name:NEVPNStatusDidChangeNotification object:nil];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
    if(error) {
        NSLog(@"Load error: %@", error);
    }}];
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.username = @“[My username]”;
p.passwordReference = [KeyChainAccess loadDataForServiceNamed:@"VIT"];
p.serverAddress = @“[My Server Address]“;
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.localIdentifier = @“[My Local identifier]”;
p.remoteIdentifier = @“[My Remote identifier]”;
p.useExtendedAuthentication = NO;
p.identityData = [My VPN certification private key];
p.disconnectOnSleep = NO;
[manager setProtocol:p];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:@"VIT VPN"];
NSArray *array = [NSArray new];
[manager setOnDemandRules: array];
NSLog(@"Connection desciption: %@", manager.localizedDescription);
NSLog(@"VPN status:  %i", manager.connection.status);
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
   if(error) {
      NSLog(@"Save error: %@", error);
   }
}];
我还在视图中放置了一个按钮,并将其TouchUpInside操作设置为以下方法:

- (IBAction)buttonPressed:(id)sender {
   NSError *startError;
   [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
   if(startError) {
      NSLog(@"Start error: %@", startError.localizedDescription);
   }
}
这里有两个问题:

1) 当我尝试保存首选项时,将抛出以下错误:
保存错误:error Domain=NEVPNErrorDomain Code=4“该操作无法完成。(NEVPNErrorDomain error 4)。”
这个错误是什么?我如何解决这个问题

2) [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];方法在调用它时不会返回任何错误,但连接状态会从断开连接变为连接,只需片刻,然后返回断开连接状态


任何帮助都将不胜感激:)

问题在于保存时出现的错误:
保存错误:错误域=NEVPNErrorDomain代码=4

如果查看NEVPNManager.h头文件,您将看到错误代码4为“NEVPNErrorConfigurationStale”。该配置已过时,需要加载。 您应该调用
loadFromPreferencesWithCompletionHandler:
,并在完成处理程序中修改要修改的值,然后调用
saveToPreferencesWithCompletionHandler:
。问题中的示例是在加载完成之前修改配置,这就是为什么会出现此错误

更像这样:

[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
     // do config stuff
     [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
     }];
}];

这个答案将有助于那些正在寻找使用网络扩展框架的解决方案的人

我的要求是使用IKEv2协议连接/断开VPN服务器(当然,您也可以通过更改vpnManager协议配置将此解决方案用于IPSec)

注意:如果您正在寻找L2TP协议,则使用网络扩展无法连接VPN服务器。请参阅:

以下是我的工作代码片段:

声明VPNManager对象和其他有用的东西

var vpnManager = NEVPNManager.shared()
var isConnected = false

@IBOutlet weak var switchConntectionStatus: UISwitch!    
@IBOutlet weak var labelConntectionStatus: UILabel!
在viewDidLoad中添加观察者以获取VPN状态,并在Keychain中存储vpnPassword,您也可以存储sharedSecret,这将需要IPSec协议

override func viewDidLoad() {

    super.viewDidLoad()

    let keychain = KeychainSwift()
    keychain.set("*****", forKey: "vpnPassword")

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.VPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil)

 }
现在,在我的应用程序中,有UISwitch来连接/断开VPN服务器

func switchClicked() {

    switchConntectionStatus.isOn = false

    if !isConnected {
        initVPNTunnelProviderManager()
    }
    else{
        vpnManager.removeFromPreferences(completionHandler: { (error) in

            if((error) != nil) {
                print("VPN Remove Preferences error: 1")
            }
            else {
                self.vpnManager.connection.stopVPNTunnel()
                self.labelConntectionStatus.text = "Disconnected"
                self.switchConntectionStatus.isOn = false
                self.isConnected = false
            }
        })
    }
}
点击交换机后,使用下面的代码启动VPN隧道

func initVPNTunnelProviderManager(){

    self.vpnManager.loadFromPreferences { (error) -> Void in

        if((error) != nil) {
            print("VPN Preferences error: 1")
        }
        else {

            let p = NEVPNProtocolIKEv2()
// You can change Protocol and credentials as per your protocol i.e IPSec or IKEv2

            p.username = "*****"
            p.remoteIdentifier = "*****"
            p.serverAddress = "*****"

            let keychain = KeychainSwift()
            let data = keychain.getData("vpnPassword")

            p.passwordReference = data
            p.authenticationMethod = NEVPNIKEAuthenticationMethod.none

//          p.sharedSecretReference = KeychainAccess.getData("sharedSecret")! 
// Useful for when you have IPSec Protocol

            p.useExtendedAuthentication = true
            p.disconnectOnSleep = false

            self.vpnManager.protocolConfiguration = p
            self.vpnManager.isEnabled = true

            self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in
                if((error) != nil) {
                    print("VPN Preferences error: 2")
                }
                else {


                    self.vpnManager.loadFromPreferences(completionHandler: { (error) in

                        if((error) != nil) {

                            print("VPN Preferences error: 2")
                        }
                        else {

                            var startError: NSError?

                            do {
                                try self.vpnManager.connection.startVPNTunnel()
                            }
                            catch let error as NSError {
                                startError = error
                                print(startError)
                            }
                            catch {
                                print("Fatal Error")
                                fatalError()
                            }
                            if((startError) != nil) {
                                print("VPN Preferences error: 3")
                                let alertController = UIAlertController(title: "Oops..", message:
                                    "Something went wrong while connecting to the VPN. Please try again.", preferredStyle: UIAlertControllerStyle.alert)
                                alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))

                                self.present(alertController, animated: true, completion: nil)
                                print(startError)
                            }
                            else {
                                self.VPNStatusDidChange(nil)
                                print("VPN started successfully..")
                            }

                        }

                    })

                }
            })
        }
    }
}
一旦VPN成功启动,您可以相应地更改状态,即通过调用VPNStatusDidChange

func VPNStatusDidChange(_ notification: Notification?) {

    print("VPN Status changed:")
    let status = self.vpnManager.connection.status
    switch status {
    case .connecting:
        print("Connecting...")
        self.labelConntectionStatus.text = "Connecting..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .connected:
        print("Connected")
        self.labelConntectionStatus.text = "Connected"
        self.switchConntectionStatus.isOn = true
        self.isConnected = true
        break
    case .disconnecting:
        print("Disconnecting...")
        self.labelConntectionStatus.text = "Disconnecting..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .disconnected:
        print("Disconnected")
        self.labelConntectionStatus.text = "Disconnected..."
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .invalid:
        print("Invalid")
        self.labelConntectionStatus.text = "Invalid Connection"
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    case .reasserting:
        print("Reasserting...")
        self.labelConntectionStatus.text = "Reasserting Connection"
        self.switchConntectionStatus.isOn = false
        self.isConnected = false

        break
    }
}
我在这里提到:


谢谢:)

奇怪,当我在beta 4设备上运行您的代码副本时,我从[NEVPNManager sharedManager]返回了一个零;啊,我没有将“个人VPN”权限添加到我的应用程序ID或权限文件中。要这样做,请转到“功能”“在项目下,您如何从钥匙链检索密码?我已将我帐户的密码存储在密钥链中,并尝试将持久引用(文档暗示这是必要的)检索到protocol.passwordReference字段中,但当我连接到VPN服务时,它仍会提示我输入密码(一旦输入密码,我就连接了,因此其他一切看起来都很好)。使用
NEVPNManager
类需要
com.apple.developer.networking.vpn.api
权限。您可以通过在Xcode中为应用程序启用“个人VPN”功能来获得应用程序的此权限。为什么不连接免费VPN ip地址目标cIt工作正常!还有一件事是,这个功能只适用于真实设备,不适用于模拟器。顺便说一句:今晚我会在博客上写这个,我会提到你,伙计;)你在beta5上试过这个吗?我有类似的代码,可以在beta 4之前使用,在beta 5上我遇到了一个错误:Domain=NEConfigurationErrorDomain code=2“Missing name”UserInfo=0x170078940{NSLocalizedDescription=Missing name}你知道“Missing name”错误吗?在xcode 6-beta 6中出现了相同的Missing name错误!有什么解决方案吗?我已经按照您的步骤进行了操作,但是我在连接到我的ikev2服务器使用的vpn时收到“意外错误”警报。crt和我已经在我的设备上安装了它,手动添加的配置工作正常,只有具有相同配置的个人vpn安装错误。crt可以同时使用添加的配置和个人vpn吗?或者我们必须为个人vpn做些其他事情才能使用证书?KeychainSwift在这里不起作用它只是将字符串作为数据返回您需要一个keychain ref@KooroshGhorbani我面临与此类相同的问题它在保存首选项时崩溃。