Apple 的 Network 框架已经发展为一个统一的、对 Swift 友好的工具包,用于在应用中构建安全、高性能的网络功能——无需 BSD 套接字的样板代码和陷阱。本文总结了 iOS 和 macOS 26 引入的新 API 与模式,提供完整代码示例,并解释它们如何与 Swift 的结构化并发模型相契合。
1. 为什么选择 Network Framework?
- 按名称连接 & Happy Eyeballs – 自动 DNS 解析与智能 IPv4/IPv6 竞速。
- 内置 TLS – 无需外部 SSL 库即可实现端到端加密。
- 移动性与代理支持 – 无缝的网络接口切换(如 Wi-Fi Assist、多路径)。
- 现代传输协议 – 原生支持 QUIC,此外还有 TCP/UDP。
- 可组合协议栈 – 以声明式方式叠加 TLS、TLV、Coder 等。
- WebSocket 支持 – 一个服务端可同时服务原生与 Web 应用。
- 结构化并发 – 所有 API 都支持
async/await,并可取消任务。
2. 创建连接
2.1 最小化 TLS 连接
1
2
3
4
5
| import Network
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLS()
}
|
- Endpoint 描述 连接的目标。
- Protocol stack 描述 如何连接。
- Parameters 则细化连接的 条件。
2.2 添加 TCP 与 IP 选项
1
2
3
4
5
6
7
8
9
10
| import Network
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLS {
TCP {
IP()
.fragmentationEnabled(false)
}
}
}
|
TCP 和 IP 默认会被推断,仅在需要自定义选项时显式声明(如禁用分片)。
2.3 自定义连接参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import Network
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029),
using: .parameters {
TLS {
TCP {
IP()
.fragmentationEnabled(false)
}
}
}
.constrainedPathsProhibited(true) // 遵循低数据模式
)
|
通过显式提供 NWParameters,可以在低数据模式下禁止使用受限网络接口。
3. 理解连接生命周期
NetworkConnection 会自动经历以下状态:
| 状态 | 说明 |
|---|
| preparing | DNS 解析、Happy Eyeballs 竞速、协议握手 |
| ready | 连接建立完成,可以安全收发数据 |
| waiting | 暂时没有可用路径(如断网) |
| failed | 不可恢复错误(会附带 Error) |
| cancelled | 任务或应用显式取消 |
可忽略状态,直接在 send/receive 时等待就绪;也可以监听状态更新,用于驱动 UI 提示。
4. 数据收发
4.1 简单的流式传输
1
2
3
4
5
6
7
8
9
10
11
12
13
| import Network
public func sendAndReceiveWithTLS() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLS()
}
let outgoingData = Data("Hello, world!".utf8)
try await connection.send(outgoingData)
let incomingData = try await connection.receive(exactly: 98).content
print("Received data: \(incomingData)")
}
|
send 会挂起,直到数据排入发送队列。 receive(exactly:) 会等待直到接收到指定字节数。
4.2 解析变长负载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import Network
public func sendAndReceiveWithTLS() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLS()
}
let outgoingData = Data("Hello, world!".utf8)
try await connection.send(outgoingData)
let remaining32 = try await connection.receive(as: UInt32.self).content
guard var remaining = Int(exactly: remaining32) else { throw MyError.invalidLength }
while remaining > 0 {
let chunk = try await connection.receive(atLeast: 1, atMost: remaining).content
remaining -= chunk.count
// 增量解码图像块
}
}
|
由于流协议不保证消息边界,需要手动定义消息帧(如长度+负载)。
5. 使用 TLV 进行消息分帧
内置的 Type-Length-Value (TLV) 分帧器保证发送的消息在接收端完整对应。
5.1 数据结构
1
2
3
4
5
6
7
8
9
| import Network
enum GameMessage: Int {
case selectedCharacter = 0
case move = 1
}
struct GameCharacter: Codable { let character: String }
struct GameMove: Codable { let row: Int; let column: Int }
|
5.2 发送
1
2
3
4
5
6
7
8
9
10
11
12
| import Network
public func sendWithTLV() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLV {
TLS()
}
}
let characterData = try JSONEncoder().encode(GameCharacter(character: "🐨"))
try await connection.send(characterData, type: GameMessage.selectedCharacter.rawValue)
}
|
5.3 接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import Network
public func receiveWithTLV() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
TLV {
TLS()
}
}
let (data, meta) = try await connection.receive()
switch GameMessage(rawValue: meta.type) {
case .selectedCharacter:
let c = try JSONDecoder().decode(GameCharacter.self, from: data)
print("角色选择: \(c)")
case .move:
let m = try JSONDecoder().decode(GameMove.self, from: data)
print("移动: \(m)")
case .none:
print("未知消息")
}
}
|
6. 使用 Coder 实现零样板序列化
在协议栈中加入 Coder,即可直接收发 Codable 类型,无需手动编解码。
6.1 统一枚举
1
2
3
4
5
6
| import Network
enum GameMessage: Codable {
case selectedCharacter(String)
case move(row: Int, column: Int)
}
|
6.2 发送
1
2
3
4
5
6
7
8
9
10
11
| import Network
public func sendWithCoder() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
try await connection.send(GameMessage.selectedCharacter("🐨"))
}
|
6.3 接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import Network
public func receiveWithCoder() async throws {
let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
let message = try await connection.receive().content
switch message {
case .selectedCharacter(let char):
print("角色选择: \(char)")
case .move(let row, let col):
print("移动: (\(row), \(col))")
}
}
|
Coder 自动处理分帧、序列化与反序列化,大幅减少自定义网络代码。
7. 监听传入连接
NetworkListener 提供高层级的“服务端套接字”抽象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import Network
public func listenForIncomingConnections() async throws {
try await NetworkListener {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
.run { connection in
for try await (message, _) in connection.messages {
// 处理每条消息
}
}
}
|
run 为每个连接生成一个新的 Task。- 监听器在父任务取消时自动结束。
8. 使用 NetworkBrowser 发现对等端
通过 NetworkBrowser 可以动态发现端点——包括 Bonjour 与新引入的 Wi-Fi Aware 技术。
1
2
3
4
5
6
7
8
9
10
11
12
13
| import Network
import WiFiAware
public func findNearbyDevice() async throws {
let endpoint = try await NetworkBrowser(
for: .wifiAware(.connecting(to: .allPairedDevices,
from: .ticTacToeService)))
.run { endpoints in
.finish(endpoints.first!)
}
// 使用 `endpoint` 创建 NetworkConnection
}
|
NetworkBrowser 只负责发现,真正的协议栈仍在创建 NetworkConnection 时指定。
9. 如何选择合适的协议栈
| 场景 | 推荐协议栈 |
|---|
| 与现有服务器通信 | 遵循服务端要求(如 IPP over TCP) |
| 自家应用/设备间通信 | Coder ➜ TLS 或 Coder ➜ QUIC |
| HTTP/HTTPS | 使用 URLSession,无需修改代码 |
10. 总结
- 新核心类型 –
NetworkConnection、NetworkListener、NetworkBrowser。 - 分帧选项 – 内置 TLV;
Coder 支持 Codable 类型。 - Swift 并发支持 – 声明式构建器、
async API、自动任务取消。 - 安全与传输组合 – TLS、QUIC、多路径、代理,全部无缝支持。
- 动态发现 – Bonjour 与跨平台 Wi-Fi Aware。
这些 API 让现代、安全、高效的网络应用开发更加简单。通过拥抱结构化并发、声明式协议栈与一流安全性,开发者可以专注于业务逻辑与用户体验——而连接管理、消息分帧与序列化交由 Network 框架处理。