Skip to content

网络

面试点

http, https ,tcp/udp, dns解析,session/cookie

http

超文本传输协议

  • 请求/响应报文
  • 链接建立流程
  • http的特点

1. 请求方法

get post head put delete options

2. get 和 post 区别

  • get请求参数?分割拼接到 URL 后面,post 请求参数在 body里面
  • get 参数长度限制 2048 个字符,post 一般没有该限制
  • get请求不安全,post请求比较安全?

语义角度回答:

  • get: 获取资源, 安全的 幂等的 可缓存的
  • post: 处理资源, 非安全的 非幂等的 不可缓存的

所谓安全性就是多次请求不会引起 server 端的任何状态变化 get head options

幂等性,同一个请求方法执行多次和执行一次的效果完全相同 put delete

请求是否可以被缓存 get head 等

3. get 请求

HTTP(超文本传输协议)请求中的 GET 和 POST 方法是最常用的两种请求方法,它们有不同的用途和特点。

GET 请求

GET 请求用于从服务器请求数据。它的主要特点如下:

  • 请求参数在 URL 中:GET 请求的参数包含在 URL 中,以查询字符串的形式出现。例如:http://example.com/page?name=value&name2=value2,查询字符串紧跟在 URL 的问号 ? 之后,每个参数用 & 分隔。

  • 幂等性:GET 请求是幂等的,多次相同的 GET 请求应该返回相同的结果,不会对服务器上的资源产生影响。

  • 请求可缓存:浏览器会缓存 GET 请求的结果。缓存有助于提高性能和减少服务器负载。

  • 数据长度限制:URL 的长度在不同浏览器和服务器中有不同的限制,通常在 2048 个字符左右。因此,GET 请求适用于发送少量数据。

  • 安全性:因为参数显示在 URL 中,敏感数据不应通过 GET 请求发送,因为它们可能会被记录在日志文件中,或被第三方工具拦截。

示例

GET /search?q=openai HTTP/1.1
Host: www.example.com

4. post 请求

POST 请求

POST 请求用于向服务器发送数据,通常用于提交表单或上传文件。它的主要特点如下:

  • 请求参数在请求体中:POST 请求的参数包含在请求体中,而不是 URL 中。因此可以发送大量数据,包括文件。

  • 非幂等性:POST 请求是非幂等的,相同的 POST 请求多次发送可能会导致服务器创建多个相同的资源(例如多次提交表单会导致多次记录)。

  • 请求不可缓存:浏览器默认不会缓存 POST 请求的结果。每次 POST 请求都会直接发送到服务器。 数据长度限制:

  • POST 请求没有数据长度限制,可以发送大量数据,这对于上传文件或提交大型表单特别有用。

  • 安全性:尽管 POST 请求参数不显示在 URL 中,但它们仍然不加密。如果需要发送敏感数据,应该在 HTTPS 协议下进行。

示例

POST /submit-form HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

name=John&age=30

详细比较

特性 GET POST
参数位置 URL 中 请求体中
幂等性
缓存 可以缓存 通常不缓存
数据长度限制 有限制 无限制
安全性 参数在 URL 中可见 参数在请求体中不可见,但不加密
用途 获取数据 发送数据(如表单提交、文件上传)

5. 选择使用场景

GET:用于请求数据,如从服务器获取网页内容、图片、视频等资源。适合发送少量的非敏感数据。

POST:用于发送数据,如提交表单、上传文件等。适合发送大量或敏感数据。

理解 GET 和 POST 方法的不同特点和用途,可以帮助你在开发过程中选择合适的请求方法,确保应用程序的安全性和性能。

6. 状态码

  • 1xx: 信息响应 (Informational Responses)
  • 2xx: 成功响应 (Successful Responses) 200ok 201create 204 nocontent
  • 3xx: 重定向 (Redirection) 301 请求的资源已永久移动到新位置,302 请求的资源临时移动到新位置(常用于临时重定向)。
  • 4xx: 客户端错误 (Client Errors) 403服务器理解请求,但拒绝执行。404请求的资源不存在。
  • 5xx: 服务器错误 (Server Errors) 500服务器内部错误,无法完成请求。502 Bad Gateway:网关或代理服务器收到无效响应。503 Service Unavailable:服务器暂时过载或维护中,无法处理请求。

7. HTTP建立连接过程

HTTP(Hypertext Transfer Protocol)是一个基于 TCP/IP 的应用层协议。HTTP 的建立连接过程主要依赖于 TCP 三次握手和四次挥手。以下是 HTTP 建立连接的详细过程:

  1. DNS 解析 在进行 HTTP 请求之前,首先需要将域名解析为 IP 地址,这一步通常由 DNS(域名系统)服务器完成。过程如下:

    • 客户端向 DNS 服务器发送域名解析请求。
    • DNS 服务器返回对应的 IP 地址。
  2. TCP 三次握手 HTTP 协议基于 TCP 协议,首先需要通过 TCP 建立一个可靠的连接,这个过程称为三次握手。

    1. 客户端发送 SYN 包:

      • 客户端向服务器发送一个 SYN(同步序列号)包,请求建立连接。
      • 包含初始序列号 Seq = x。
    2. 服务器发送 SYN-ACK 包:

      • 服务器收到 SYN 包后,回复一个 SYN-ACK 包,表示同意连接。
      • 包含服务器的初始序列号 Seq = y 和对客户端序列号的确认 Ack = x + 1。
    3. 客户端发送 ACK 包:

      • 客户端收到 SYN-ACK 包后,再发送一个 ACK(确认)包给服务器。
      • 务器序列号的确认 Ack = y + 1。

通过这三次交互,客户端和服务器之间建立了一个可靠的 TCP 连接。

  1. 发送 HTTP 请求 一旦 TCP 连接建立,客户端可以发送 HTTP 请求。一个典型的 HTTP GET 请求如下:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
  1. 服务器处理请求并返回响应 服务器收到 HTTP 请求后,会处理请求并返回响应。一个典型的 HTTP 响应如下:
HTTP/1.1 200 OK
Date: Mon, 15 May 2023 18:25:00 GMT
Content-Type: text/html
Content-Length: 1234

<!DOCTYPE html>
<html>
<head>
    <title>Example Page</title>
</head>
<body>
    <h1>Hello, World!</h1>
</body>
</html>

  1. 连接的保持和关闭

    • 保持连接:HTTP/1.1 默认启用持久连接(Keep-Alive),即同一个 TCP 连接可以被重复使用以发送多个请求/响应对。客户端和服务器可以通过 Connection: keep-alive 头来维持连接。
    • 关闭连接:如果不再需要维持连接,客户端或服务器可以发送 Connection: close 头,指示对方在完成当前请求/响应后关闭连接。
  2. TCP 四次挥手 当 HTTP 请求和响应处理完成,需要关闭 TCP 连接时,进行四次挥手:

    1. 客户端发送 FIN 包:

      • 客户端发送一个 FIN(结束)包,表示不再发送数据,但仍能接收数据。
      • 包含序列号 Seq = u。
    2. 服务器发送 ACK 包:

      • 服务器收到 FIN 包后,回复一个 ACK 包,表示确认。
      • 包含对客户端序列号的确认 Ack = u + 1。
    3. 服务器发送 FIN 包:

      • 服务器发送一个 FIN 包,表示自己也不再发送数据,但能接收数据。
      • 包含序列号 Seq = v。
    4. 客户端发送 ACK 包:

      • 客户端收到服务器的 FIN 包后,回复一个 ACK 包。
      • 包含对服务器序列号的确认 Ack = v + 1。

通过这四次交互,TCP 连接被安全地关闭。

总结 HTTP 建立连接的过程包括 DNS 解析、TCP 三次握手、发送 HTTP 请求、服务器处理请求并返回响应、保持或关闭连接、TCP 四次挥手。理解这些步骤有助于更好地掌握网络通信的原理和性能优化。

HTTP 状态码(HTTP Status Codes)是服务器在响应客户端请求时返回的数字代码,用于表示请求的处理结果。状态码分为五类,每个类用一个特定的数字范围表示。以下是各类状态码的详细说明:

HTTP

1. 特点

无连接: http 的持久连接 无状态: cookie / session

2. 持久连接

头部字段:

  • connnection: keep-alive

  • time: 20 // 20秒内不会关闭

  • max: 10 // 最多建立多少个连接

如何判断一个请求是否结束:

  • content-length: 1024

  • chunked, 最后会有一个空的 chunked

HTTPS与网络安全

1.HTTPS和HTTP有怎么样的区别

  • HTTPS = HTTP+ SSL/TLS
  • SSL/TLS 是应用层之下,传输层之上

2.HTTPS 连接的建立流程

HTTPS 连接的建立流程主要包括了 SSL/TLS 握手过程。这个过程确保客户端和服务器之间的通信是安全和加密的。以下是 HTTPS 建立连接的详细流程:

  1. 客户端发起请求(Client Hello)

    • 客户端(如浏览器)向服务器发送 Client Hello 消息,包含:

      • 客户端支持的 SSL/TLS 版本。
      • 客户端支持的加密套件(cipher suites)。
      • 一个随机数(client random),用于后续密钥生成。
      • 支持的压缩方法(如果有)。
      • 可选的扩展信息(如 SNI,用于指示访问的服务器主机名)。
  2. 服务器响应(Server Hello)

    • 服务器收到 Client Hello 后,发送 Server Hello 消息,包含:

      • 服务器选择的 SSL/TLS 版本。
      • 服务器选择的加密套件。
      • 另一个随机数(server random),用于后续密钥生成。
      • 服务器的数字证书(包含公钥)。
      • 可选的扩展信息。
  3. 服务器证书验证

    • 客户端接收到服务器的 Server Hello 消息后,验证服务器的数字证书。
    • 数字证书由受信任的证书颁发机构(CA)签名,包含服务器的公钥。
    • 客户端使用本地存储的受信任的 CA 列表验证证书的真实性。
    • 如果证书无效,客户端会显示警告信息,提示用户连接不安全。
  4. 客户端密钥交换(Client Key Exchange)

    • 客户端生成一个称为 Pre-Master Secret 的随机数,并使用服务器的公钥加密这个随机数。
    • 加密后的 Pre-Master Secret 被发送到服务器。
  5. 服务器解密 Pre-Master Secret

    • 服务器使用自己的私钥解密 Pre-Master Secret
    • 服务器和客户端现在都拥有三个值:client randomserver randomPre-Master Secret
  6. 会话密钥生成

    • 客户端和服务器使用 client randomserver randomPre-Master Secret 生成对称会话密钥。
    • 这个会话密钥将用于加密后续的会话数据,确保传输的机密性和完整性。
  7. 客户端完成(Client Finished)

    • 客户端发送一个 Finished 消息,表明客户端的握手过程结束。
    • 这条消息是使用会话密钥加密的,目的是验证密钥交换和加密算法是否正确。
  8. 服务器完成(Server Finished)

    • 服务器也发送一个 Finished 消息,表明服务器的握手过程结束。
    • 这条消息同样是使用会话密钥加密的,进一步验证整个握手过程是否成功。
  9. 安全通信

    • 握手过程完成后,客户端和服务器开始使用生成的对称会话密钥进行加密通信。
    • 所有后续的 HTTP 数据传输都经过加密,确保数据的机密性和完整性。

这个流程确保了客户端和服务器之间的通信是加密的,并且对中间人攻击等安全威胁具有防御能力。

会话密钥: random s + random c + 预主密钥

HTTPS 都使用了哪些加密手段? 为什么

  • 连接建立过程使用非对称加密,非对称加密很耗时
  • 后续通信过程使用了对称加密

3. 非对称加密

4. 对称加密

对称密钥会在网络中传递,如果劫持,就暴露了

5. 总结

在 HTTPS 中,使用了非对称加密和对称加密两种加密技术。每种加密技术在 HTTPS 连接的不同阶段发挥不同的作用:

非对称加密 (Asymmetric Encryption)

特点:

  • 使用一对公钥和私钥进行加密和解密。
  • 公钥用于加密,私钥用于解密。
  • 加密和解密的过程较慢,但适用于小数据量的加密。

在 HTTPS 中的使用:

  1. 证书验证:

    • 服务器使用公钥加密客户端生成的 Pre-Master Secret
    • 只有服务器拥有私钥,能够解密这个 Pre-Master Secret
  2. 身份验证:

    • 服务器通过提供由可信的证书颁发机构(CA)签发的证书,客户端通过验证该证书确保服务器的身份。

对称加密 (Symmetric Encryption)

特点:

  • 使用相同的密钥进行加密和解密。
  • 加密和解密过程较快,适合大数据量的加密。

在 HTTPS 中的使用:

  1. 数据传输:

    • 在握手过程中,客户端和服务器共同生成一个对称密钥(会话密钥)。
    • 握手完成后,所有的通信数据(HTTP 请求和响应)都使用这个对称密钥进行加密和解密。

HTTPS 中的加密流程

结合前面的解释,HTTPS 中的加密流程可以详细描述如下:

  1. 握手阶段:

    • 客户端发起请求: 客户端发送 Client Hello 消息,包括客户端支持的 SSL/TLS 版本、加密套件等。
    • 服务器响应: 服务器发送 Server Hello 消息,选择加密套件,并提供数字证书(包含公钥)。
    • 客户端验证证书: 客户端验证服务器的数字证书。
    • 客户端密钥交换: 客户端生成 Pre-Master Secret,并使用服务器的公钥加密后发送给服务器。
    • 服务器解密 Pre-Master Secret: 服务器使用私钥解密 Pre-Master Secret
    • 生成会话密钥: 客户端和服务器使用 Pre-Master Secret 生成对称会话密钥。
  2. 数据传输阶段:

    • 使用对称加密: 握手完成后,客户端和服务器使用生成的对称会话密钥对所有传输数据进行加密和解密。

总结

  • 非对称加密:

    • 用于握手阶段,主要是保护 Pre-Master Secret,以及验证服务器身份。
    • 使用服务器的公钥加密客户端生成的 Pre-Master Secret
  • 对称加密:

    • 用于数据传输阶段,保护实际的 HTTP 数据。
    • 使用握手阶段生成的对称会话密钥进行加密和解密。

通过结合这两种加密技术,HTTPS 提供了安全、高效的通信方式,确保数据传输的机密性和完整性。

UDP/TCP

1. UDP

特点: 无连接,尽最大努力交付, 面相报文

  • UDP 是一种无连接协议。
  • 每个数据报(datagram)都独立传输,包含源地址和目标地址。
  • 不保证数据包的顺序到达,也不保证可靠传输(数据包可能丢失、重复或乱序)。
  • 适用于对数据传输可靠性要求不高,但要求速度快的场景,如视频流、在线游戏和实时语音通信。

功能: 复用,分用,差错检测

2. TCP

特点: 面相连接, 可靠传输,面相字节流,流量控制,拥塞控制

面相连接: 三次握手

为啥不是两次握手? 如果第一次握手超时了,后来又到达 server 端了,是不是建立了两次连接,资源浪费了。

四次挥手

TCP 可靠传输

  • 无差错
  • 不丢失
  • 不重复
  • 按序到达

停止等待协议: 无差错情况, 超时重传, 确认丢失,确认迟到

无差错情况

超时重传

确认丢失

确认迟到

面相字节流

TCP(传输控制协议)是面向字节流的协议,这意味着它处理的数据是一连续的字节流,而不是离散的消息或包。理解 TCP 面向字节流的概念有助于更好地理解其工作原理和如何使用它进行网络编程。以下是详细的解释:

  1. 连续的字节流

    • 在 TCP 连接中,发送方和接收方之间的数据传输被视为一个无序的、连续的字节流。
    • 不同于面向消息的协议(如 UDP),TCP 不保留发送的数据边界。也就是说,如果你通过 TCP 发送一段数据,它们可能会在接收端以不同的边界读取。
  2. 无固定消息边界

    • TCP 发送的数据可能会根据底层网络的情况进行分段,并以不同大小的段(segment)发送给接收方。
    • 接收方接收到的可能是多个 TCP 段拼接的结果,应用程序需要自行处理这些数据,以提取出有意义的消息。

示例

假设在 TCP 连接中,客户端向服务器发送以下三段数据:

# Client side
sock.send(b'Hello, ')
sock.send(b'World!')
sock.send(b' How are you?')

在服务器端,这些数据可能会以不同的方式接收,具体取决于网络情况和接收缓冲区的大小:

# Server side
data = sock.recv(1024)  # 可能接收到 b'Hello, World! How are you?'

也可能分开接收:

# Server side
data1 = sock.recv(7)  # 接收到 b'Hello, '
data2 = sock.recv(6)  # 接收到 b'World!'
data3 = sock.recv(12) # 接收到 b' How are you?'

面向字节流的影响

  1. 应用层协议

    • 由于 TCP 不保留消息边界,应用程序需要设计自己的协议来定义数据的开始和结束。
    • 常见的方法包括:

      • 使用固定长度的消息。
      • 在消息前面添加一个头部,说明消息的长度。
      • 使用特定的分隔符来分隔消息。
  2. 缓冲处理

    • TCP 使用发送和接收缓冲区来处理数据流。在应用程序调用 sendrecv 函数时,数据可能会被缓存,而不会立即发送或接收。
    • 应用程序需要处理可能的部分数据读取或写入的情况,确保数据的完整性。

TCP 的其他特性

  1. 可靠性

    • TCP 保证数据的可靠传输,包括数据包的顺序和完整性。如果数据包丢失、重复或乱序,TCP 会自动处理并重传。
  2. 流量控制

    • TCP 使用流量控制机制来防止发送方发送数据过快,使接收方来不及处理,从而避免接收缓冲区溢出。
  3. 拥塞控制

    • TCP 实现拥塞控制算法来防止网络拥塞,确保网络资源的合理使用。

总结

TCP 面向字节流的特点决定了它不关心应用程序发送的数据是如何划分的,而是将这些数据作为一个连续的字节流进行传输。应用程序需要自行处理数据的边界和消息的完整性。在实际使用中,通过设计应用层协议和合理的缓冲处理,可以确保通过 TCP 传输的数据被正确解释和处理。

DNS 解析

域名到 ip 地址的映射, DNS 解析请求采用 UDP 数据报,且明文

递归查询:

迭代查询:

dns解析遇到的问题?

  • 劫持
  • dns解析转发问题

DNS劫持与HTTP的关系是怎样的?

没有关系 DNS解析发生在HTTP建立连接之前 DNS解析请求使用UDP数据报,端口号53

如何解决 DNS 劫持?

  • httpDNS
  • 长连接

就是用 http 协议去获取 ip 地址了

HTTPDNS

HTTPDNS 是一种通过 HTTP 协议进行 DNS 解析的方法,它通常用于绕过 DNS 劫持和提高解析的准确性和安全性。

工作原理

  • 客户端请求:客户端向 HTTPDNS 服务器发送一个 HTTP 请求,包含要解析的域名。
  • HTTPDNS 服务器响应:HTTPDNS 服务器处理请求,查询相应的 IP 地址,并以 HTTP 响应的形式返回给客户端。

优点

  • 防止 DNS 劫持:传统 DNS 请求通过 UDP 发送,容易被拦截和劫持。HTTPDNS 请求通过 HTTP/HTTPS 发送,减少了劫持风险。
  • 提高解析速度:HTTPDNS 可以集成 CDN 服务,减少 DNS 解析时间。
  • 穿透限制:通过 HTTP/HTTPS 发送 DNS 请求,可以穿透防火墙和其他网络限制。

长连接

长连接(Long Connection)是一种保持客户端和服务器之间连接不断开的通信方式,通常用于需要频繁通信的场景。

工作原理

  • 建立连接:客户端与服务器建立一个 TCP 连接,并保持该连接不关闭。
  • 保持连接:客户端和服务器通过该连接进行多次数据交换,而无需为每次请求重新建立连接。
  • 定期心跳:通过发送心跳包来检测连接状态并保持连接活跃。

优点

  • 减少连接开销:避免了每次请求都需要建立和关闭连接的开销,提高了效率。
  • 提高传输效率:在高频率通信的场景中,长连接可以显著减少延迟。
  • 稳定性:长连接通过心跳机制检测连接状态,确保通信的稳定性。

session/cookie

客户端发送的cookie在http请求报文的Cookie首部字段中

服务器端设置http响应报文的Set-Cookie首部字段

如何修改 cookie

  • 新cookie覆盖|日cookie
  • 覆盖规则:name、path、domain等需要与原cookie一致

如何删除 cookie

  • 新cookie覆盖|日cookie
  • 覆盖规则:name、path、domain等需要与原cookie一致
  • 设置cookie的expires=过去的一个时间点,或者maxAge=0

怎么保证 cookie 安全?

  • 对Cookie进行加密处理
  • 只在https上携带Cookie
  • 设置Cookie为httpOnly,防止跨站脚本攻击

2. session

Session也是用来记录用户状态,区分用户的;状态存放在服务器端。

Session和Cookie的关系是怎样的?

  • Session需要依赖于Cookie机制


在 iOS 开发中,优化网络请求、检测代理、测量 DNS 时间和使用自建证书是提升应用安全性和性能的关键点。这里是具体的实现方式:


1. 检测是否使用代理

有些应用(特别是金融、支付类)需要检测用户是否使用了代理服务器,以防止流量劫持。可以通过以下几种方式检测:

(1)检查系统代理设置

func isUsingProxy() -> Bool {
    if let proxySettings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?,
       let proxies = proxySettings["HTTPEnable"] as? Int, proxies == 1 {
        return true
    }
    return false
}

🚀 优化:可以进一步检查 HTTPProxyHTTPSProxy 是否有值。


(2)检测 SOCKS 代理

func isUsingSOCKSProxy() -> Bool {
    guard let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary? else { return false }

    if let proxyDict = settings["__SCOPED__"] as? NSDictionary {
        for key in proxyDict.allKeys {
            if let keyString = key as? String, keyString.contains("SOCKS") {
                return true
            }
        }
    }
    return false
}

(3)检测 VPN(可选)

VPN 也可能影响网络请求,可通过检查 utunpppipsec 之类的接口来判断:

func isUsingVPN() -> Bool {
    guard let interfaces = CNCopySupportedInterfaces() as? [String] else { return false }
    for interface in interfaces {
        if let dict = CNCopyCurrentNetworkInfo(interface as CFString) as NSDictionary?,
           let _ = dict[kCNNetworkInfoKeySSID as String] {
            return false
        }
    }
    return true
}

综合检测代理

func isNetworkIntercepted() -> Bool {
    return isUsingProxy() || isUsingSOCKSProxy()
}

2. 测试 DNS 请求时间

(1)使用 URLSessionTaskMetrics 监测 DNS 解析时间

从 iOS 10 开始,URLSessionTaskMetrics 提供了 domainLookupStartDatedomainLookupEndDate,可以直接计算 DNS 解析时间:

class NetworkMonitor: NSObject, URLSessionTaskDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        guard let transactionMetrics = metrics.transactionMetrics.first else { return }

        if let start = transactionMetrics.domainLookupStartDate, let end = transactionMetrics.domainLookupEndDate {
            let dnsLookupTime = end.timeIntervalSince(start) * 1000 // 转换成毫秒
            print("DNS 解析时间: \(dnsLookupTime) ms")
        }
    }
}

优化: - 可以通过 AlamofireEventMonitor 实现类似功能。 - 监测多个请求的 DNS 时间,分析不同网络环境下的表现。


3. 使用自建证书

如果你的服务器使用的是自签名证书,而非 CA 颁发的证书,需要手动信任该证书,否则请求会失败。

(1)使用 URLSession 手动验证自签名证书

class CustomSessionDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            let credential = URLCredential(trust: serverTrust)
            completionHandler(.useCredential, credential)
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
}

let session = URLSession(configuration: .default, delegate: CustomSessionDelegate(), delegateQueue: nil)

⚠️ 注意:这样做会绕过 iOS 的证书信任机制,生产环境不建议这样做,建议使用正确的 CA 证书。


(2)绑定证书(SSL Pinning)

如果你想确保请求只使用你的服务器证书,而不会被中间人攻击(MITM),可以用 SSL Pinning 绑定证书:

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    guard let serverTrust = challenge.protectionSpace.serverTrust else {
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
    let remoteCertificateData = SecCertificateCopyData(certificate!) as Data

    if let localCertPath = Bundle.main.path(forResource: "my_cert", ofType: "cer"),
       let localCertData = try? Data(contentsOf: URL(fileURLWithPath: localCertPath)),
       remoteCertificateData == localCertData {
        let credential = URLCredential(trust: serverTrust)
        completionHandler(.useCredential, credential)
    } else {
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

🚀 优化: - Alamofire 也支持 SSL Pinning,可通过 ServerTrustManager 配置。 - 可以使用 SHA256 进行证书指纹匹配,而不是直接比对 .cer 文件。


4. 网络请求优化

(1)减少 DNS 查询

  • 使用 IP 直连,避免 DNS 查询(如果适用)。
  • 预解析 DNS:通过 NSLookupCFHost 提前解析域名。

(2)减少 HTTPS 连接建立时间

  • 开启 HTTP/2(大多数服务器已经支持)。
  • 保持 TCP 连接存活Keep-Alive)。
  • 减少 SSL/TLS 握手延迟
  • 使用 Session Resumption,复用 TLS 连接。
  • 在服务器端启用 OCSP Stapling,减少证书验证时间。

(3)提高并发性能

如果多个 API 请求可以并行执行,使用 URLSession 并发请求:

let queue = DispatchQueue(label: "network", attributes: .concurrent)
queue.async {
    fetchData(from: "https://api.example.com/data1")
}
queue.async {
    fetchData(from: "https://api.example.com/data2")
}

(4)缓存优化

  • 减少不必要的网络请求
  • 使用 URLCache 进行请求缓存。
  • 通过 ETag 机制避免重复下载相同内容。
  • 本地缓存静态资源
  • 例如图片、CSS、JS 文件,避免频繁请求服务器。

(5)低网环境优化

  • 检测网络状态
  • NWPathMonitor (iOS 12+) 监听网络变化,切换 WiFi/4G/5G。
  • Reachability 库检测网络是否可用。
  • 断点续传
  • 使用 NSURLSessionDownloadTask 实现大文件断点续传。

总结

优化项 方法
检测代理 CFNetworkCopySystemProxySettings
DNS 解析时间 URLSessionTaskMetrics
自签名证书 URLSessionDelegate 处理 SSL Pinning
优化网络请求 HTTP/2、缓存优化、并发请求
低网环境优化 NWPathMonitor、断点续传

你现在在做哪一部分优化?可以具体聊聊需求,我可以提供更针对性的建议!🚀


在 iOS 网络请求优化中,MetricKitMXMetricManager)和 NSURLSessionTaskMetrics 是两种用于 监测和分析网络性能 的工具,它们可以提供关于请求的详细信息。

1. NSURLSessionTaskMetrics(单个请求级别)

NSURLSessionTaskMetrics 主要用于 监测某个具体请求的性能,包括: - 请求开始时间结束时间DNS 解析时间TCP 连接时间TLS 握手时间 等关键步骤的耗时。 - 重定向次数。 - 是否使用了 HTTP/2、代理、复用连接等

获取 NSURLSessionTaskMetrics

URLSessiondelegate 方法中可以获取:

class NetworkMonitor: NSObject, URLSessionTaskDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        for transaction in metrics.transactionMetrics {
            print("请求 URL: \(transaction.request.url?.absoluteString ?? "")")
            print("DNS 解析时间: \(transaction.domainLookupEndDate?.timeIntervalSince(transaction.domainLookupStartDate ?? Date()) ?? 0) 秒")
            print("TCP 连接时间: \(transaction.connectEndDate?.timeIntervalSince(transaction.connectStartDate ?? Date()) ?? 0) 秒")
            print("TLS 握手时间: \(transaction.secureConnectionEndDate?.timeIntervalSince(transaction.secureConnectionStartDate ?? Date()) ?? 0) 秒")
            print("请求开始时间: \(transaction.requestStartDate?.timeIntervalSince(transaction.domainLookupEndDate ?? Date()) ?? 0) 秒")
            print("服务器响应时间: \(transaction.responseStartDate?.timeIntervalSince(transaction.requestStartDate ?? Date()) ?? 0) 秒")
            print("数据传输时间: \(transaction.responseEndDate?.timeIntervalSince(transaction.responseStartDate ?? Date()) ?? 0) 秒")
            print("是否复用了连接: \(transaction.isReusedConnection)")
            print("是否使用了代理: \(transaction.proxyConnection ? "是" : "否")")
        }
    }
}

NSURLSessionTaskMetrics 主要获取的内容 | 指标 | 描述 | |----------|----------| | redirectCount | 请求重定向的次数 | | transactionMetrics | 请求的所有网络层级时间数据 | | domainLookupStartDatedomainLookupEndDate | DNS 解析时间 | | connectStartDateconnectEndDate | TCP 连接时间 | | secureConnectionStartDatesecureConnectionEndDate | TLS/SSL 握手时间 | | requestStartDateresponseStartDate | 请求 - 响应时间 | | responseStartDateresponseEndDate | 数据传输时间 | | isReusedConnection | 是否复用了 HTTP 连接 | | proxyConnection | 是否通过代理发送请求 | | networkProtocolName | 使用的网络协议(HTTP/1.1、HTTP/2) |


2. MetricKit(全局级别)

MetricKit (MXMetricManager) 提供 全局的网络请求性能指标,适用于 监测应用整体网络状态,而不是单个请求。

获取 MetricKit 统计数据

import MetricKit

class NetworkMetricsHandler: NSObject, MXMetricManagerSubscriber {
    override init() {
        super.init()
        MXMetricManager.shared.add(self)
    }

    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            if let networkMetrics = payload.networkTransferMetrics {
                print("总上行数据: \(networkMetrics.cumulativeCellularUpload.bytesSent) bytes")
                print("总下行数据: \(networkMetrics.cumulativeCellularDownload.bytesReceived) bytes")
                print("WIFI 上行数据: \(networkMetrics.cumulativeWiFiUpload.bytesSent) bytes")
                print("WIFI 下行数据: \(networkMetrics.cumulativeWiFiDownload.bytesReceived) bytes")
            }
        }
    }
}

MetricKit 主要获取的内容 | 指标 | 描述 | |----------|----------| | cumulativeCellularUpload.bytesSent | 移动网络上行数据量 | | cumulativeCellularDownload.bytesReceived | 移动网络下行数据量 | | cumulativeWiFiUpload.bytesSent | WiFi 上行数据量 | | cumulativeWiFiDownload.bytesReceived | WiFi 下行数据量 | | averageUploadThroughput | 平均上行吞吐量 | | averageDownloadThroughput | 平均下行吞吐量 |

适用场景 - 监测应用的整体带宽使用情况,防止超量使用移动流量。 - 分析 WiFi vs 移动网络性能,优化请求策略(如 WiFi 下加载高清图,4G 下压缩)。 - 分析应用的长期网络吞吐量,避免不必要的数据浪费。


3. URLSessionTaskMetrics vs MetricKit

对比项 NSURLSessionTaskMetrics MetricKit
作用范围 单个请求 全局应用级别
数据获取时机 请求结束时 系统定期收集(数小时级别)
提供的指标 DNS 时间、TLS 握手、TCP 连接时间、请求耗时、重定向、是否复用连接等 总数据流量、带宽、WiFi vs 4G 统计
适用场景 优化单个请求的性能 分析应用整体的网络表现

4. 进阶优化策略

🔹 减少 DNS 解析时间 - 启用 URLSession 连接缓存 - 使用 IP 直连或 DNS 预解析

🔹 优化 TLS 连接 - 使用 TLS 1.3(更快) - 保持长连接,减少握手次数

🔹 优化请求流程 - 启用 HTTP/2 复用连接 - 使用 NSURLSession 连接池 - 避免不必要的 redirect

🔹 降低数据流量 - 压缩数据(gzip、brotli) - 使用 MetricKit 分析 WiFi / 4G 消耗 - 后台任务限制流量


5. 总结

工具 适用场景 主要获取的信息
NSURLSessionTaskMetrics 分析单个请求 DNS 解析时间、TCP 连接时间、TLS 握手时间、是否使用代理、是否复用连接、HTTP 版本
MetricKit 分析全局网络表现 总流量、带宽消耗、WiFi vs 4G 使用情况
  • 如果你想优化单个请求的性能NSURLSessionTaskMetrics 能提供最详细的数据。
  • 如果你想优化整个 App 的网络使用情况MetricKit 能帮你长期分析趋势。

🚀 你现在是想优化单个请求,还是整个 App 的网络流量?可以结合你的需求深入优化! 😃