iOS 关于开发VPN中遇到的坑
首先澄清下:由于服务器的节点是国内的,所以我们的APP并不是违法,所以别太关注这个问题。
在此先感谢大佬以及大佬的文章:https://www.jianshu.com/p/5ed93a8a1449
他的是swift的工程,我是采用主工程oc,插件工程swift编写的
例子中很多代码都是引用这位大佬的
1.vpn开发所需配置、环境
1.1下载extension模板。
默班1.2创建一个extension工程
new->target->选中上图所示的pakettunelprovider,然后语言选择swift
创建extension
1.3证书文件权限配置
这里注意现在已经不需要申请权限,只需要把appid开通Network Extensions和Personal VPN这两个玩意
然后根据这个appid生成对应的描述文件,将描述文件配置到xcode对应的工程
注意,extension工程也要简历单独的appid和描述文件
1.4不出意外的话就会这样了
image.png然后extension和主工程的capabilitis要开启
主工程和extension
2.vpn代码配置
2.1在主工程中配置connect和disconnect
connect(这里的配置文件NEKitRule.conf是从nekit库里拿的默认的)
- (void)connect{
[self loadAndCreatePrividerManager:^(NETunnelProviderManager *manager) {
if (!manager) {
return ;
}
NSError *error;
[manager.connection startVPNTunnelWithOptions:@{} andReturnError:&error];
if (error) {
NSLog(@"start error");
}else{
NSLog(@"rsssss");
}
}];
}
- (void)loadAndCreatePrividerManager:(void(^)(NETunnelProviderManager *manager))compelte{
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
NETunnelProviderManager *manager = [[NETunnelProviderManager alloc] init];
if (managers.count>0) {
manager = managers.firstObject;
if (managers.count>1) {
for (NETunnelProviderManager* manager in managers) {
[manager removeFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
NSLog(@"remove dumplicate VPN config successful!");
}else{
NSLog(@"remove dumplicate VPN config failed with %@", error);
}
}];
}
}
}else{
manager = [self createProviderManager];
}
manager.enabled = YES;
//set rule config
NSMutableDictionary *conf = @{}.mutableCopy;
conf[@"ss_address"] = @"xxx.xx.xx.xxx";
conf[@"ss_port"] = @xxxx;//注意是number值、、、
conf[@"ss_method"] = @"RC4MD5";// 大写 没有横杠 看Extension中的枚举类设定 否则引发fatal error
conf[@"ss_password"] = @"你的服务器密码";
conf[@"ymal_conf"] = [self getRuleConf];
NETunnelProviderProtocol *orignConf = (NETunnelProviderProtocol *)manager.protocolConfiguration;
orignConf.providerConfiguration = conf;
manager.protocolConfiguration = orignConf;
//save vpn
[manager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
//注意这里保存配置成功后,一定要再次load,否则会导致后面StartVPN出异常
[manager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
NSLog(@"save vpn success");
compelte(manager);return;
}
compelte(nil);return;
}];
}else{
compelte(nil);return;
}
}];
}];
}
- (void)disconnect{
[self loadProviderManager:^(NETunnelProviderManager *manager) {
[manager.connection stopVPNTunnel];
}];
}
- (NSString *)getRuleConf{
NSString * Path = [[NSBundle mainBundle] pathForResource:@"NEKitRule" ofType:@"conf"];
NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:Path]];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
disconnect
- (void)disconnect{
[self loadProviderManager:^(NETunnelProviderManager *manager) {
[manager.connection stopVPNTunnel];
}];
}
#pragma mark - private method
- (void)loadProviderManager:(void(^)(NETunnelProviderManager *manager))pm{
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if (managers.count > 0) {
pm(managers.firstObject);
return ;
}
return pm(nil);
}];
}
实际使用中代码不止这么多,还有很多需要监听的地方,有空的看下面的demo
2.2在插件工程配swift库
这里就开始难了,难得不是代码,是库的配置。
首先extension中的swift代码可以自己写,当然插件也可以用oc的,但是我不会,而且也没有时间成本,我用swift写插件是为了使用NEKit这个库,这个库封装了shadowsocket中的很多东西,所以使用起来还是比较方便的。
然鹅这个库需要通过Cartfile进行集成,cartfile使用方法:https://www.jianshu.com/p/a734be794019
集成完之后会有这个文件夹,里面有很多framwork
然后重点来了
-
把主工程配置这个位置添加上刚才那几个framwork的路径
主工程
-
把extension工程这个位置拉进来上framwork
extension.png
到此应该额外配置没了的
2.3在extension工程编码
这里我是直接用的顶部那位大佬demo里的东西全部代码,里面代码很容易理解,大家可以看看
extension代码
3.开发中遇到的坑
3.1虽然我没遇到,但是还是要说,注意这里的加密格式,所以要在主工程写的时候注意下
switch method{
case "AES128CFB":algorithm = .AES128CFB
case "AES192CFB":algorithm = .AES192CFB
case "AES256CFB":algorithm = .AES256CFB
case "CHACHA20":algorithm = .CHACHA20
case "SALSA20":algorithm = .SALSA20
case "RC4MD5":algorithm = .RC4MD5
default:
fatalError("Undefined algorithm!")
}
3.1要开启手机中app的无线和数据网权限
写demo的时候默认没开启,连接了好久都连接不上😌
3.1extension和主工程都要9.3以上,nekit要求9.3以上
3.2 端口号用number不是string
连接上秒断开就是因为这个坑逼。。。
3.3设置只能在app里startvpn,不能通过设置打开vpn
这里需要用到startvpn方法里的options参数
[manager.connection startVPNTunnelWithOptions:@{NEVPNConnectionStartOptionUsername:@"xxxx"} andReturnError:&error]
//在插件里判断是不是在app里启动
if let name = options?[NEVPNConnectionStartOptionUsername]{
NSLog((name as! String) + "df")
}else{
exit(EXIT_FAILURE)
}
插件代码判断
这里注意刚才大佬的demo里包括我的demo里的swift插件都有一个kvo,那里也有个startvpn,其中的options,也要改掉
也要改掉
3.4 注意nekit 的framwork的 buildversion 不是一个integer值,要改一下,不然打包上传的时候会报错
如果有能帮助到大家的地方,希望能点个赞,顺便给github上点个赞
demo