下面这个类,在 >= iOS10 的操作系统才可以运作,部分属性在 >= iOS13 才可以使用,我分别解释下他们的意思,但是 有些属性,我是真的不知道是干什么的,经过尝试,没有变化,这里我就不做解释。
如果觉得我是在这瞎 BB,可以去 navigate to NSURLSessionTaskTransactionMetrics define 自己查看,上面备注写的也很清楚了。
API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0))
@interface NSURLSessionTaskTransactionMetrics : NSObject
// URLRequest 请求的Body和Header都在这里面呢,包含请求的全部信息。
@property (copy, readonly) NSURLRequest *request;
// URLResponse 相应的信息都在这个对象里面
@property (nullable, copy, readonly) NSURLResponse *response;
// 请求任务开始前,可以理解为整个请求的最开始的时间点。
@property (nullable, copy, readonly) NSDate *fetchStartDate;
// 开始DNS解析的时间点
@property (nullable, copy, readonly) NSDate *domainLookupStartDate;
// 结束DNS解析的时间点
@property (nullable, copy, readonly) NSDate *domainLookupEndDate;
// 建立连接的起始点
@property (nullable, copy, readonly) NSDate *connectStartDate;
// 建立 SSL/TLS 对话密钥的起始时间
@property (nullable, copy, readonly) NSDate *secureConnectionStartDate;
// 建立 SSL/TLS 对话密钥的终点时间
@property (nullable, copy, readonly) NSDate *secureConnectionEndDate;
// 建立连接的终点
@property (nullable, copy, readonly) NSDate *connectEndDate;
// 建立好连接通道后,请求开始的时间点
@property (nullable, copy, readonly) NSDate *requestStartDate;
// 建立好连接通道后,请求结束的时间点
@property (nullable, copy, readonly) NSDate *requestEndDate;
// 开始得到响应的时间点
@property (nullable, copy, readonly) NSDate *responseStartDate;
// 接收完最后一字节的数据的响应结束时间点
@property (nullable, copy, readonly) NSDate *responseEndDate;
// 网络协议的名称
@property (nullable, copy, readonly) NSString *networkProtocolName;
// 是否使用了网络代理
@property (assign, readonly, getter=isProxyConnection) BOOL proxyConnection;
// 是否重用了连接
@property (assign, readonly, getter=isReusedConnection) BOOL reusedConnection;
// 资源获取的类型,有缓存,Network,不知道,等
@property (assign, readonly) NSURLSessionTaskMetricsResourceFetchType resourceFetchType;
// iOS13+ 发送的请求头占的字节
@property (readonly) int64_t countOfRequestHeaderBytesSent API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 发送的Body占的字节
@property (readonly) int64_t countOfRequestBodyBytesSent API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 发送的Body在Encoding之前占的字节
@property (readonly) int64_t countOfRequestBodyBytesBeforeEncoding API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 响应头占字节数
@property (readonly) int64_t countOfResponseHeaderBytesReceived API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 响应Body体占字节数
@property (readonly) int64_t countOfResponseBodyBytesReceived API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ Decoding之后响应消息体占字节数
@property (readonly) int64_t countOfResponseBodyBytesAfterDecoding API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 本地IP地址
@property (nullable, copy, readonly) NSString *localAddress API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 本地端口
@property (nullable, copy, readonly) NSNumber *localPort API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 访问服务器机器的IP地址
@property (nullable, copy, readonly) NSString *remoteAddress API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 访问服务器机器的端口号
@property (nullable, copy, readonly) NSNumber *remotePort API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ TLS协议的版本
@property (nullable, copy, readonly) NSNumber *negotiatedTLSProtocolVersion API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ TLS选择的加密协议
@property (nullable, copy, readonly) NSNumber *negotiatedTLSCipherSuite API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 不知道,我不论怎么切换网络环境一直都是 NO
@property (readonly, getter=isCellular) BOOL cellular API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 不知道,我不论怎么切换网络环境一直都是 NO
@property (readonly, getter=isExpensive) BOOL expensive API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 不知道,我不论怎么切换网络环境一直都是 NO
@property (readonly, getter=isConstrained) BOOL constrained API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
// iOS13+ 不知道,我不论怎么切换网络环境一直都是 NO
@property (readonly, getter=isMultipath) BOOL multipath API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
这里还是要感谢苹果爸爸,虽然 iOS10 才给出来,但是现在已经 9102 年了,以支持最近 3 个版本的原则,我们的用户也应该都是 iOS10+ 的了,所以这个官方提供的功能,用的安全放心,还简单,不用去 hook 这,hook 那,然后得到的数据还不准,还让工程复杂化。
NSURLSession *session = [NSURLSession
sessionWithConfiguration: config
delegate:self delegateQueue:nil];
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
if ([metrics.transactionMetrics count] > 0) {
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
NSLog(@"========================================================================");
if (obj.domainLookupStartDate && obj.domainLookupEndDate) {
int dnsLookupTime = ceil([obj.domainLookupEndDate timeIntervalSinceDate:obj.domainLookupStartDate] * 1000);
NSLog(@"DNS解析时长 单位ms:%d",dnsLookupTime);
}
if (obj.connectStartDate && obj.connectEndDate) {
int tcpTime = ceil([obj.connectEndDate timeIntervalSinceDate:obj.connectStartDate] * 1000);
NSLog(@"TCP+SSL整个连接建立的时间 单位ms:%d",tcpTime);
}
if (obj.secureConnectionEndDate && obj.secureConnectionStartDate) {
int sslTime = ceil([obj.secureConnectionEndDate timeIntervalSinceDate:obj.secureConnectionStartDate] * 1000);
NSLog(@"SSL四次握手时长 单位ms:%d",sslTime);
}
if (obj.requestStartDate && obj.responseEndDate) {
int transmissionTime = ceil([obj.responseEndDate timeIntervalSinceDate:obj.requestStartDate] * 1000);
NSLog(@"传输时间 单位ms:%d",transmissionTime);
}
if (obj.fetchStartDate && obj.responseEndDate) {
int requestTime = ceil([obj.responseEndDate timeIntervalSinceDate:obj.fetchStartDate] * 1000);
NSLog(@"完整请求时长 单位ms:%d",requestTime);
}
if (obj.fetchStartDate) {
UInt64 requestDate = [obj.fetchStartDate timeIntervalSince1970] * 1000;
NSLog(@"开始发起请求的时间点:%llu",requestDate);
}
if (obj.responseEndDate) {
UInt64 responseEndDate = [obj.responseEndDate timeIntervalSince1970] * 1000;
NSLog(@"请求结束的时间点:%llu",responseEndDate);
}
NSLog(@"HTTP请求类型:%@",obj.request.HTTPMethod);
NSLog(@"协议名称:%@",obj.networkProtocolName);
NSLog(@"请求URL:%@",[obj.request.URL absoluteString]);
NSLog(@"是否使用代理:%d",obj.isProxyConnection);
NSLog(@"是否重用连接获取资源:%d",obj.reusedConnection);
// iOS13 或者 iOS13 以上才可以使用
NSLog(@"服务器IP:%@",obj.remoteAddress);
NSLog(@"服务器端口:%@",obj.remotePort);
NSLog(@"请求的Header字节数:%lld bytes",obj.countOfRequestHeaderBytesSent);
NSLog(@"请求的Body字节数:%lld bytes",obj.countOfRequestBodyBytesSent);
NSLog(@"请求的Encoding之前的Body字节数:%lld bytes",obj.countOfRequestBodyBytesBeforeEncoding);
NSLog(@"响应的Header字节数:%lld bytes",obj.countOfResponseHeaderBytesReceived);
NSLog(@"响应的Body字节数:%lld bytes",obj.countOfResponseBodyBytesReceived);
NSLog(@"响应的Decoding之后的Body字节数:%lld bytes",obj.countOfResponseBodyBytesAfterDecoding);
// 网络类型
// NSURLSessionTaskMetricsResourceFetchTypeUnknown
// NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad
// NSURLSessionTaskMetricsResourceFetchTypeServerPush
// NSURLSessionTaskMetricsResourceFetchTypeLocalCache
}
}];
}
}
但是我发现一个问题,requestStartDate 比 requestEndDate 晚, 例如打印如下: requestStartDate:1576467073.038733 requestEndDate:1576467071.590589 按照 WWDC 上面所描述的,应该 start 先于 end,但是实际测试结果并不是这样。