美文网首页iOS开发笔记iOS
iOS 关于开发VPN中遇到的坑

iOS 关于开发VPN中遇到的坑

作者: moxacist | 来源:发表于2018-04-19 09:16 被阅读3372次

    首先澄清下:由于服务器的节点是国内的,所以我们的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
    然后重点来了
    • 把主工程配置这个位置添加上刚才那几个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

    相关文章

      网友评论

        本文标题:iOS 关于开发VPN中遇到的坑

        本文链接:https://www.haomeiwen.com/subject/bvjbkftx.html