李峰峰博客

AFNetworking 实现分析

2019-10-11

一、概述

AFNetworking 是 iOS 中常用的网络请求库,其使用也比较简单,以 POST 请求为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 创建 AFHTTPSessionManager 实例
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

// 设置请求序列化器为 JSON
manager.requestSerializer = [AFJSONRequestSerializer serializer];

// 设置响应序列化器为 JSON
manager.responseSerializer = [AFJSONResponseSerializer serializer];

// 请求参数
NSDictionary *parameters = @{@"title": @"foo", @"body": @"bar", @"userId": @1};

// 发送 POST 请求
[manager POST:@"https://www.lixkit.com/posts"
parameters:parameters
headers:nil
progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
// 请求成功的回调
NSLog(@"POST 请求成功,响应数据:%@", responseObject);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
// 请求失败的回调
NSLog(@"POST 请求失败,错误信息:%@", error);
}
];

AFNetworking(4.0) 核心类可以按照下图进行分层:

各层职责:

  • AFHTTPSessionManager
    • 继承自 AFURLSessionManager,提供对外 API 处理常见的 HTTP 请求(如 GETPOSTPUTDELETE 等)
  • AFURLSessionManager
    • 提供对 NSURLSession 的封装,基于 NSURLSession 提供了实际的 HTTP 请求实现
  • AFHTTPRequestSerializer
    • 用于将请求参数序列化为适合 HTTP 请求体的格式,支持 JSONXMLProperty List 等格式的请求序列化
  • AFHTTPResponseSerializer
    • 用于将 HTTP 响应数据反序列化为常见的数据结构,解析 JSONXMLProperty List 等格式的响应数据
  • AFNetworkReachabilityManager
    • 用于监控和报告当前网络连接状态
  • AFSecurityPolicy
    • 管理和验证服务器的安全策略

二、源码解读

1、AFHTTPSessionManager/AFURLSessionManager

POST 请求为例,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// POST 请求方法
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
// 创建一个 POST 请求任务
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:uploadProgress
downloadProgress:nil
success:success
failure:failure];

// 启动任务
[dataTask resume];

// 返回任务对象
return dataTask;
}

// 创建并返回一个 NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSError *serializationError = nil;

// 使用请求序列化器创建 NSMutableURLRequest 对象
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method
URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
parameters:parameters
error:&serializationError];

// 设置请求头
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}

// 如果序列化出错,调用失败回调并返回 nil
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}

__block NSURLSessionDataTask *dataTask = nil;

// 创建 NSURLSessionDataTask,提供 block 形式回调
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
// 任务完成后的回调
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];

return dataTask;
}

根据上述逻辑可以看出 AFNetworking 是基于 NSURLSession 实现的网络请求,AFURLSessionManagerAFHTTPSessionManager 的父类,AFURLSessionManagerNSURLSession 及其任务(包括 NSURLSessionUploadTask)进行了封装,并将原本通过 NSURLSessionDelegate 接收的回调改为了使用 block 回调的形式。

2、AFHTTPRequestSerializer

AFHTTPSessionManager 中持有了 AFHTTPRequestSerializer 的实例,在发起网络请求前可以配置 requestSerializer 参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

// ...

@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;


@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

// ...

@end

AFHTTPRequestSerializer 负责将请求参数序列化为适合 HTTP 请求体的格式,它提供了对 HTTP 请求的高级封装,支持多种序列化格式和请求配置,例如:

1
2
3
4
5
6
7
8
AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];

// 设置请求头
[requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];

// 设置请求超时时间
requestSerializer.timeoutInterval = 30;

AFHTTPRequestSerializer 有两个子类:AFJSONRequestSerializerAFPropertyListRequestSerializer,两个子类都实现了 AFURLRequestSerialization 协议。

AFURLRequestSerialization 协议中声明了如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>

/**
返回一个包含指定参数的请求,该请求是原始请求的副本,并将参数编码到请求中。

@param request 原始请求。
这是一个 `NSURLRequest` 对象,表示最初的 HTTP 请求。
@param parameters 要编码的参数。
这是一个可选的参数,通常是一个 `NSDictionary` 或 `NSArray`,包含需要添加到请求中的参数。
@param error 尝试编码请求参数时发生的错误。
这是一个指向 `NSError` 对象的指针,用于在编码过程中发生错误时传递错误信息。

@return 一个序列化后的请求。
返回一个新的 `NSURLRequest` 对象,其中包含了编码后的参数。如果编码过程中发生错误,返回 `nil`。
*/
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end

这个方法的主要功能是将指定的参数编码到一个新的 NSURLRequest 中,并返回这个新的 NSURLRequest

以 AFJSONRequestSerializer 为例,其对该方法的实现如下:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 确保 request 参数不为空
NSParameterAssert(request);

// 如果请求方法在需要将参数编码到 URI 的方法列表中,直接调用父类的方法进行序列化
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}

// 创建一个可变的 NSMutableURLRequest 对象,复制自原始请求
NSMutableURLRequest *mutableRequest = [request mutableCopy];

// 设置 HTTP 请求头,如果原始请求中没有设置相应的字段
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];

// 如果有参数需要编码
if (parameters) {
// 如果 Content-Type 头字段没有设置,默认设置为 application/json
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}

// 检查参数是否为有效的 JSON 对象
if (![NSJSONSerialization isValidJSONObject:parameters]) {
if (error) {
// 如果参数不是有效的 JSON 对象,设置错误信息并返回 nil
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
return nil;
}

// 将参数序列化为 JSON 数据
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];

// 如果序列化过程中发生错误,返回 nil
if (!jsonData) {
return nil;
}

// 设置请求体为序列化后的 JSON 数据
[mutableRequest setHTTPBody:jsonData];
}

// 返回包含序列化参数的新请求
return mutableRequest;
}

可以看出,上述实现主要是将请求参数序列化为 JSON 格式,并将请求头的 Content-Type 设置为 application/json

开发时,可以直接使用 AFJSONRequestSerializerAFPropertyListRequestSerializer,也可以实现 AFURLRequestSerialization 协议自定义一个 requestSerializer

1
2
3
4
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

// 直接使用 AFJSONRequestSerializer,也可以自定义
manager.requestSerializer = [AFJSONRequestSerializer serializer];

总结下 AFHTTPRequestSerializer 有两个子类的作用:

  • AFJSONRequestSerializer
    • 将请求参数序列化为 JSON 格式
    • 它会将请求头的 Content-Type 设置为 application/json
  • AFPropertyListRequestSerializer
    • 将请求参数序列化为 Property List 格式
    • 自动设置 Content-Typeapplication/x-plist

3、AFHTTPResponseSerializer

AFHTTPRequestSerializer 一样,AFHTTPSessionManager 中也持有了 AFHTTPResponseSerializer 的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

// ...

@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;


@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

// ...

@end

上述 responseSerializerNSURLSessionTaskDelegate 的 HTTP 请求完成的方法中调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma mark - NSURLSessionTaskDelegate

- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// ...

if (error) {

// ...

} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

// ...

});
}
}

可以看到,上述调用了 responseSerializerresponseObjectForResponse:data:error: 这个方法,这个方法实际上是 AFURLResponseSerialization 协议中声明的方法。

AFHTTPRequestSerializer 类似,AFHTTPResponseSerializer 也有多个子类,每个子类都实现了 AFURLResponseSerialization 协议中的如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>

/**
从与指定响应关联的数据中解码出响应对象。

@param response 要处理的响应。
这是一个 `NSURLResponse` 对象,包含响应的元数据,如状态码和头信息。这个参数可以为 `nil`。
@param data 要解码的响应数据。
这是一个 `NSData` 对象,包含服务器返回的原始数据。这个参数可以为 `nil`。
@param error 尝试解码响应数据时发生的错误。
这是一个指向 `NSError` 对象的指针,用于在解码过程中发生错误时传递错误信息。如果解码成功,这个参数应为 `nil`。如果解码失败,这个参数将包含具体的错误信息。

@return 从指定响应数据中解码出的对象。
返回一个解码后的对象,通常是 `NSDictionary`、`NSArray`、`UIImage` 等。如果解码过程中发生错误,返回 `nil`。
*/
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end

简而言之,该方法的作用是将 HTTP 的 response 解析成指定的数据类型并返回。

以其中一个子类 AFJSONResponseSerializer 为例,其实现的该方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#pragma mark - AFURLResponseSerialization

/**
解析并返回从服务器响应数据中解码出的对象。

@param response 要处理的响应。
这是一个 `NSURLResponse` 对象,包含响应的元数据,如状态码和头信息。
@param data 要解码的响应数据。
这是一个 `NSData` 对象,包含服务器返回的原始数据。
@param error 尝试解码响应数据时发生的错误。
这是一个指向 `NSError` 对象的指针,用于在解码过程中发生错误时传递错误信息。如果解码成功,这个参数应为 `nil`。如果解码失败,这个参数将包含具体的错误信息。

@return 从指定响应数据中解码出的对象。通常是 `NSDictionary` 或 `NSArray` 对象。如果解码过程中发生错误,返回 `nil`。
*/
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 验证响应和数据是否有效,如果无效则返回 nil
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
// 如果错误不存在或错误域是 NSURLErrorCannotDecodeContentData 或 AFURLResponseSerializationErrorDomain,则返回 nil
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}

// 处理 Rails 返回单个空格作为 `head :ok` 的情况,这是 Safari 中的一个 bug
// 详见 https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];

// 如果数据长度为 0 或数据是单个空格,则返回 nil
if (data.length == 0 || isSpace) {
return nil;
}

NSError *serializationError = nil;

// 尝试将数据解析为 JSON 对象
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];

// 如果解析失败,设置错误信息并返回 nil
if (!responseObject)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}

// 如果配置为移除包含 null 值的键,则移除这些键
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}

// 返回解析后的对象
return responseObject;
}

上述主要逻辑是将服务器返回的原始数据(NSData)解码为 JSON 对象(如 NSDictionaryNSArray),并进行必要的验证和错误处理。

AFHTTPResponseSerializer 内置了如下几个子类:

  • AFJSONResponseSerializer
    • 将服务器返回的 JSON 数据序列化为 NSDictionaryNSArray 对象
  • AFXMLParserResponseSerializer
    • 将服务器返回的 XML 数据序列化为 NSXMLParser 对象
  • AFPropertyListResponseSerializer
    • 将服务器返回的 Property List 数据序列化为 NSDictionaryNSArray 对象
  • AFImageResponseSerializer
    • 将服务器返回的图像数据序列化为 UIImage 对象
  • AFCompoundResponseSerializer
    • 允许组合多个响应序列化器,根据不同的 MIME 类型处理响应

也可以实现 AFURLResponseSerialization 自定义 responseSerializer

4、AFNetworkReachabilityManager

(1)实现方式

AFNetworkReachabilityManager 是 AFNetworking 实现的一个工具类,用于检测当前网络是否可达,以及网络连接类型的变化(如 WiFi 或蜂窝网络)。

主要功能如下:

  • 监控网络状态变化:检测网络连接是否可达。
  • 监听网络连接类型:区分 WiFi 和蜂窝网络。
  • 提供回调接口:在网络状态变化时执行指定的回调。

AFNetworkReachabilityManager 使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建并启动网络可达性管理器
AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];

// 设置网络状态变化回调
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"网络不可达");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"通过 WiFi 连接");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"通过蜂窝网络连接");
break;
case AFNetworkReachabilityStatusUnknown:
default:
NSLog(@"未知网络状态");
break;
}
}];

// 启动网络可达性监听
[reachabilityManager startMonitoring];

AFNetworkReachabilityManager 基于系统的 SCNetworkReachability API 实现,其 startMonitoringstopMonitoring 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
开始监控

这个方法会首先停止任何现有的网络状态监控,然后设置新的监控回调,并将其添加到主线程的 RunLoop 中,以便在网络状态变化时能够触发回调。

@note 这个方法会异步获取当前的网络状态,并触发状态变化回调。
*/
- (void)startMonitoring {
// 停止现有的网络状态监控
[self stopMonitoring];

// 如果 networkReachability 不存在,则直接返回
if (!self.networkReachability) {
return;
}

// 弱引用 self,避免在 block 中产生循环引用
__weak __typeof(self)weakSelf = self;
// 定义网络状态变化时的回调 block
AFNetworkReachabilityStatusCallback callback = ^(AFNetworkReachabilityStatus status) {
// 强引用 self,确保在 block 内 self 不会被释放
__strong __typeof(weakSelf)strongSelf = weakSelf;

// 更新网络状态
strongSelf.networkReachabilityStatus = status;
// 如果定义了网络状态变化回调 block,则调用它
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}

return strongSelf;
};

// 定义 SCNetworkReachabilityContext,上下文信息包括回调 block
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
// 设置网络状态变化时的回调函数
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
// 将 networkReachability 添加到主线程的 RunLoop 中,开始监控网络状态变化
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

// 异步获取当前的网络状态,并触发状态变化回调
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),^{
SCNetworkReachabilityFlags flags;
// 获取当前的网络状态标志位
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
// 触发网络状态变化回调
AFPostReachabilityStatusChange(flags, callback);
}
});
}


/**
停止监控

这个方法会将 networkReachability 从主线程的 RunLoop 中移除,从而停止网络状态的监控。
*/
- (void)stopMonitoring {
// 如果 networkReachability 不存在,则直接返回
if (!self.networkReachability) {
return;
}

// 将 networkReachability 从主线程的 RunLoop 中移除,停止网络状态监控
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

(2)SCNetworkReachability

SCNetworkReachability 是苹果提供的一个系统级 API,用于检测网络连接状态和监控网络连接的变化。它是 System Configuration 框架的一部分,适用于 iOS 和 macOS 应用程序。

主要功能如下:

  • 检测网络连接状态:判断当前网络是否可达。
  • 监控网络状态变化:监听网络连接状态的变化,并在状态变化时触发回调。

SCNetworkReachability 使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <SystemConfiguration/SystemConfiguration.h>

// 回调函数,当网络状态变化时调用
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
if (flags & kSCNetworkReachabilityFlagsReachable) {
if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
printf("网络通过蜂窝网络连接\n");
} else {
printf("网络通过 WiFi 连接\n");
}
} else {
printf("网络不可达\n");
}
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建 SCNetworkReachability 引用
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com");

// 设置回调函数
SCNetworkReachabilityContext context = {0, NULL, NULL, NULL, NULL};
SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context);

// 将 SCNetworkReachability 引用添加到 RunLoop 中
SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);

// 运行 RunLoop
CFRunLoopRun();

// 释放 SCNetworkReachability 引用
CFRelease(reachability);
}
return 0;
}

SCNetworkReachability 主要基于系统的网络栈和通知机制,使用时需要将 SCNetworkReachability 引用添加到 RunLoop 中,以确保系统能够在网络状态变化时触发回调。

SCNetworkReachability 使用系统的网络栈来检测网络连接状态,包括是否有网络连接、连接类型(WiFi 或蜂窝网络)等。

网络栈是操作系统中负责网络通信的一组协议和软件模块的集合。它处理从应用层到物理层的所有网络通信任务,包括数据封装、路由、传输控制等。对于 SCNetworkReachability 来说,它主要依赖于网络栈中的 IP 层和链路层来检测网络连接状态。

SCNetworkReachability 主要通过两种方式检查网络连接状态:

  • 通过发送网络探测请求(如 ICMP ping)
  • 使用系统的路由表来确定目标主机或 IP 地址是否可达:查询路由表,查看是否存在到达目标主机或 IP 地址的有效路径。如果存在有效路径,则目标主机或 IP 地址被认为是可达的。

5、AFSecurityPolicy

AFSecurityPolicy 也是被 AFHTTPSessionManager 持有的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

// ...

@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;


@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

// ...

@end

AFSecurityPolicy 主要负责验证服务器的证书,确保网络通信的安全性。通过自定义安全策略,开发者可以灵活地控制 SSL/TLS 连接的验证过程。

主要作用:

  • 证书验证:验证服务器证书的有效性。
  • 公钥验证:验证服务器的公钥。
  • 支持自定义证书:允许使用自定义证书进行验证。
  • 配置不同的验证模式:通过 SSL/TLS 连接中的证书链来验证服务器的身份,提供三种验证模式:不验证、验证证书、验证公钥。
    • AFSSLPinningModeNone
      • 不进行 SSL Pinning 验证,只依赖系统默认的证书验证机制。
      • 适用于不需要额外安全验证的场景。
    • AFSSLPinningModeCertificate
      • 验证服务器提供的证书是否与本地预置的证书匹配。
      • 适用于需要确保服务器证书与预期证书完全一致的场景。
    • AFSSLPinningModePublicKey
      • 验证服务器提供的证书中的公钥是否与本地预置的公钥匹配。
      • 适用于需要确保服务器公钥与预期公钥一致的场景。

使用 AFSecurityPolicy 自定义证书的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import <AFNetworking/AFNetworking.h>

- (void)configureSecurityPolicy {
// 获取自定义证书的路径
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"my_custom_certificate" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];

// 创建一个安全策略,使用证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

// 允许使用无效或自签名证书
securityPolicy.allowInvalidCertificates = YES;

// 不验证证书域名
securityPolicy.validatesDomainName = NO;

// 设置自定义证书
securityPolicy.pinnedCertificates = [NSSet setWithObject:certData];

// 配置 AFHTTPSessionManager 使用自定义的安全策略
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;

// 发起网络请求
[manager GET:@"https://lixkit.com" parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"请求成功: %@", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"请求失败: %@", error);
}];
}

AFSecurityPolicy 主要实现源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
@implementation AFSecurityPolicy

// 从指定的 bundle 中获取所有的证书
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
// 获取 bundle 中所有的 .cer 文件路径
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];

// 创建一个可变集合,用于存储证书数据
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
// 读取证书数据
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}

// 返回包含所有证书数据的集合
return [NSSet setWithSet:certificates];
}

// 创建一个默认的安全策略(不进行 SSL Pinning)
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;

return securityPolicy;
}

// 根据指定的 Pinning 模式创建安全策略,并使用默认的证书
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
// 从主 bundle 中获取所有证书
NSSet <NSData *> *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]];
return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates];
}

// 根据指定的 Pinning 模式和证书集合创建安全策略
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;

// 设置自定义证书
[securityPolicy setPinnedCertificates:pinnedCertificates];

return securityPolicy;
}

// 初始化方法
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}

// 默认验证域名
self.validatesDomainName = YES;

return self;
}

// 设置自定义证书
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;

if (self.pinnedCertificates) {
// 创建一个可变集合,用于存储公钥
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
// 提取证书中的公钥
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
// 设置公钥集合
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}

#pragma mark -

// 评估服务器信任
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
// 如果域名存在,允许无效证书,验证域名且不进行 SSL Pinning 或没有自定义证书
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
NSLog(@"为了验证自签名证书的域名,必须使用 pinning。");
return NO;
}

// 创建一个策略数组,用于设置服务器信任策略
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}

// 设置服务器信任策略
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

// 如果不进行 SSL Pinning
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) {
return NO;
}

switch (self.SSLPinningMode) {
case AFSSLPinningModeCertificate: {
// 创建一个可变数组,用于存储自定义证书
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// 设置自定义证书为信任的锚点证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

// 验证服务器信任链
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}

// 获取服务器信任链中的证书
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);

// 检查服务器证书是否包含在自定义证书中
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}

return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
// 获取服务器信任链中的公钥
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

// 检查服务器公钥是否包含在自定义公钥中
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}

default:
return NO;
}

return NO;
}

@end

上述主要逻辑如下:

  • 获取证书
    • certificatesInBundle: 方法从指定的 bundle 中获取所有的 .cer 证书,并返回证书数据的集合。
  • 创建默认安全策略
    • defaultPolicy 方法创建一个默认的安全策略,不进行 SSL Pinning。
  • 根据 Pinning 模式创建安全策略
    • policyWithPinningMode: 方法根据指定的 Pinning 模式创建安全策略,并使用默认的证书。
    • policyWithPinningMode:withPinnedCertificates: 方法根据指定的 Pinning 模式和证书集合创建安全策略。
  • 初始化方法
    • init 方法初始化安全策略实例,并默认设置 validatesDomainName 为 YES。
    • validatesDomainName 属性决定了在验证服务器证书时,是否需要检查证书中的域名(Common Name 或 Subject Alternative Name)是否与请求的域名匹配。这种验证可以防止中间人攻击(MITM),确保服务器的身份是可信的。
  • 设置自定义证书
    • setPinnedCertificates: 方法设置自定义证书,并提取证书中的公钥。
  • 评估服务器信任
    • evaluateServerTrust:forDomain: 方法评估服务器的信任,根据不同的 Pinning 模式(AFSSLPinningModeNoneAFSSLPinningModeCertificateAFSSLPinningModePublicKey)进行相应的验证。
Tags: 源码