美文网首页程序员iOS开发之常用技术点
iOS:NSURLProtocol搭建网络请求监控系统

iOS:NSURLProtocol搭建网络请求监控系统

作者: GCS_DEVELOPER | 来源:发表于2018-11-29 15:53 被阅读18次

    这篇文章介绍了些什么?

    通过这篇文章,你将会了解到一种对原代码毫无入侵的网络请求性能监控方案NSURLProtocol
    以及:

    1.NSURLProtocol是什么和其在URL Loading System中的作用

    2.NSURLProtocol中最重要的几个API介绍

    3.如何在集成AFNetworking等第三方网络库的项目中使用NSURLProtocol

    4.如何通过NSURLProtocol处理自定义的scheme,而不发送真正的网络请求

    一、什么是NSURLProtocol?

    1.URL Loading System

    URL-loading-system.png

    援引一段官网介绍:

    注:本文出现的中文版Apple文档均为Google翻译结果,可能有些语病,但基本上不影响阅读和理解,想阅读原文,可以点击本文提供的超链

    0.png

    简而言之:URL Loading System的作用就是与服务器进行通信

    2.URL Loading System中的Protocol

    5.jpg

    NSURLProtocol作为Client和Server的中间层,接收Client发送的Request,将其发送至Server端,并接收Server端发送的Response,将数据传回Client端

    2.NSURLProtocol官方文档

    1.png 2.png 3.png 4.png

    概要:NSURLProtocol虽然命名为Protocol但其实它是一个抽象类,正如文档介绍所说,不要直接实例化NSURLProtocol,正确的使用姿势是,创建NSURLProtocol的子类,通过registerClass:方法将其注册到URL Loading System中,系统会创建协议对象来处理相应的URL请求,我们可以通过NSURLProtocol提供的API接口,来达到存储和检索特定协议的请求数据的目的。

    3.NSURLProtocol适用的场景

    ①基于URL Loading System的网络请求

    ②UIWebView

    二、NSURLProtocol的工作流程

    1.核心API介绍

    NSURLProtocol.h

    ①拦截请求并发送

    在URL Loading System发送网络请求后,通过protocol中拦截请求并处理,再手动的将请求发送出去

    @interface NSURLProtocol : NSObject

    //所有注册此Protocol的请求都会经过这个方法的判断
    + (BOOL)canInitWithRequest:(NSURLRequest *)request;
    
    //对需要拦截的请求进行自定的处理
    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
    
    /**
        初始化protocol实例,所有来源的请求都以NSURLRequest形式接收
        
        @param client The NSURLProtocolClient object that serves as the
        interface the protocol implementation can use to report results back
        to the URL loading system
    */
    - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client;
    
    /**
        开始请求
        在这里需要我们手动的把请求发出去,可以使用原生的NSURLSessionDataTask,也可以使用的第三方网络库
        同时设置"NSURLSessionDataDelegate"协议,接收Server端的响应
    */
    - (void)startLoading;
    
    //请求被停止
    - (void)stopLoading;
    

    ②接收响应并转发

    当接收到Server端的响应时,将其通过"NSURLProtocolClient"协议,转发给URL Loading System

    @protocol NSURLProtocolClient <NSObject>

    //发生重定向时
    - (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;
    
    //接收到响应时
    - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;
    
    //接收到数据时
    - (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;
    
    //成功
    - (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;
    //失败
    - (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;
    
    

    "NSURLProtocolClient"的协议方法与"NSURLSessionDataDelegate"的协议方法基本上是一一对应的

    - (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;为例:

    /**
        1️⃣我们在`- (void)startLoading`方法中,实现了"NSURLSessionDataTask"的协议
        在接收到Sever端的响应时,首先响应"NSURLSessionDataTask"的协议方法
    */
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
    {
        //2️⃣通过NSURLProtocolClient转发给URL Loading System
        [[self client] URLProtocol:self didLoadData:data];
    }
    

    ③注册Protocol到URL loading system

    在实现上述方式后,通过[NSURLProtocol registerClass:self]将protocol注册到URL loading system中

    2.注意事项

    ①线程同步问题

    URL Loading System发出请求与接收响应要在同一线程

    //创建NSURLSessionDataTask实例
    - (instancetype)initWithTask:(NSURLSessionDataTask *)task delegate:(id<NSURLSessionDataDelegate>)delegate modes:(NSArray *)modes
    {
        self = [super init];
        if (self != nil) {
            //在开始请求前记录当前线程
            self->_thread = [NSThread currentThread];
        }
        return self;
    }
    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
    {
        //在接收响应时,同步到发送请求时线程
        [self performSelector:@selector(performBlockOnClientThread:) onThread:self.thread withObject:[block copy] waitUntilDone:NO modes:self.modes];
    }
    

    ②AFNetworking、Alamofire等第三方网络库发出的网络请求无法进入到自定义NSURLProtocol的问题

    以AFNetworking举例:

     NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
     NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
    

    NSURLSessionConfiguration作为参数初始化的NSURLSession,在configuration对象中有属性protocolClasses

    @property (nullable, copy) NSArray<Class> *protocolClasses;
    

    protocolClasses官方文档

    6.png

    也就是说,我们监控网络是通过注册NSURLProtocol来实现的,但是通过sessionWithConfiguration:得到的session,它的configuration中已经有一个NSURLProtocol,所以它不会走我们的protocol中来!

    解决方案:

    创建NSURLSessionConfiguration的子类,hookprotocolClassesget方法,将我们的protocol返回

    - (NSArray *)protocolClasses {
        // 如果还有其他的监控protocol,也可以在这里加进去
        return @[[CustomHTTPProtocol class]];
    }
    

    3.两种场景

    ①真正的网络请求

    ②模拟的网络请求

    - (void)startLoading中不发送真正的网络请求,而是对自定义的请求进行解析!

    比如通过自定协议实现页面的跳转。在实际业务中,会有这样的需求,前端页调起APP,回传给客户端一个自定的协议,客户端解析协议,并跳转至对应页,假如自定协议:gcsdeveloper://handoff/openControllerA

    我们可以通过Protocol实现类似URL请求的方式来跳转至ControllerA

    只需要两步:

    1.在canInitWithRequest中判断Scheme == gcsdeveloper时返回YES,拦截请求

    2.在startLoading中发现是自定义协议,不发送真正的网络请求,而且走自定协议的解析流程

    三、NSURLProtocol的应用

    统计APP内所有网络请求的失败率、响应时间等数据

    通过拦截后统一重发,可以在对应时机添加方法,在这个过程中记录各类数据

    其他应用

    1.防止DNS劫持

    2.自定义请求和响应

    3.本地Mock数据

    4.网络的缓存处理

    5.重定向网络请求

    6.过滤掉一些非法请求

    7.使UIWebView的网络图片也享受XXWebImage的图片缓存池

    四、NSURLProtocol的不足

    1.并不是真正的拦截了所有的网络请求,比如WKWebview、AVPlayer等发出的网络请求并不能被拦截

    2.系统没有提供对个性化配置的网络请求的兼容,需要手动处理(Hook)

    3.增加一定的网络延迟,主要在于方法间的调用

    五、参考内容

    官方文档

    NSProtocol苹果官方Demo

    NSURLProtocol官方文档

    protocolClasses官方文档

    URL Loading System

    相关文章

    iOS H5容器的一些探究(二):iOS下的黑魔法NSURLProtocol

    iOS应用内抓包、NSURLProtocol 拦截 APP 内的网络请求

    iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求

    如何进行 HTTP Mock(iOS)

    特别鸣谢:谷歌翻译

    相关文章

      网友评论

        本文标题:iOS:NSURLProtocol搭建网络请求监控系统

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