iOS开发笔记iOS

iOS 关于开发VPN中遇到的坑

2018-04-19  本文已影响3372人  moxacist

首先澄清下:由于服务器的节点是国内的,所以我们的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和描述文件

image.png
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

image.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

上一篇 下一篇

猜你喜欢

热点阅读