关于 axiom-networking-legacy
面向支持 iOS 12–25 应用的 Swift 网络参考技能,介绍使用回调处理程序的 NWConnection 模式,涵盖 TLS、UDP 批量发送、监听器和 Bonjour 发现。
主要功能
- 01使用 NWConnection 建立 TLS 连接
- 02批量发送 UDP 数据报
- 03通过 NWListener 接收连接
- 04发现 Bonjour 服务
适用场景
- 01维护面向 iOS 12–25 的应用
- 02使用 TLS 加密网络连接
- 03降低频繁发送 UDP 的开销
- 04发现本地网络中的对等服务
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-networking-legacy安装后在 Claude Code 中输入/axiom-networking-legacy确认已加载。
详细介绍
Legacy iOS 12-25 NWConnection 模式
这些模式使用带完成处理程序的 NWConnection,适用于支持 iOS 12-25 的应用。如果你的应用面向 iOS 26+,请改用支持 async/await 的 NetworkConnection(参见 axiom-network-framework-ref skill)。
模式 2a:使用 TLS 的 NWConnection(iOS 12-25)
适用场景 支持 iOS 12-25,需要 TLS 加密,暂时无法使用 async/await
耗时 10-15 分钟
推荐:带完成处理程序的 NWConnection
import Network
// Create connection with TLS
let connection = NWConnection(
host: NWEndpoint.Host("mail.example.com"),
port: NWEndpoint.Port(integerLiteral: 993),
using: .tls // TCP inferred
)
// Handle connection state changes
connection.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
print("Connection established")
self?.sendInitialData()
case .waiting(let error):
print("Waiting for network: \(error)")
// Show "Waiting..." UI, don't fail immediately
case .failed(let error):
print("Connection failed: \(error)")
case .cancelled:
print("Connection cancelled")
default:
break
}
}
// Start connection
connection.start(queue: .main)
// Send data with pacing
func sendData() {
let data = Data("Hello, world!".utf8)
connection.send(content: data, completion: .contentProcessed { [weak self] error in
if let error = error {
print("Send error: \(error)")
return
}
// contentProcessed callback = network stack consumed data
// This is when you should send next chunk (pacing)
self?.sendNextChunk()
})
}
// Receive exact byte count
func receiveData() {
connection.receive(minimumIncompleteLength: 10, maximumLength: 10) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
print("Received \(data.count) bytes")
// Process data...
self?.receiveData() // Continue receiving
}
}
}
与 NetworkConnection 的主要区别
- 所有完成处理程序中都必须使用
[weak self],以防止产生保留环 - stateUpdateHandler 接收状态,而非异步序列
- send/receive 使用完成回调,而非 async/await
适用场景
- 支持 iOS 12-15(截至 2024 年覆盖 70% 的设备)
- 尚未使用 async/await 的代码库
- 需要向后兼容的库
迁移到 NetworkConnection(iOS 26+)
- stateUpdateHandler -> connection.states 异步序列
- 完成处理程序 -> try await 调用
- [weak self] -> 不再需要(async/await 会处理取消)
模式 2b:NWConnection UDP 批量发送(iOS 12-25)
适用场景 支持 iOS 12-25,需要高效发送多个 UDP 数据报,并希望将 CPU 使用率降低约 30%
耗时 10-15 分钟
背景 传统 UDP 套接字每次系统调用发送一个数据报。如果你要发送 100 个小数据包,就会发生 100 次上下文切换。批量发送可将其减少到约 1 次系统调用。
不推荐:逐个发送 UDP 数据报(CPU 使用率高)
// WRONG — 100 context switches for 100 packets
for frame in videoFrames {
sendto(socket, frame.bytes, frame.count, 0, &addr, addrlen)
// Each send = context switch to kernel
}
推荐:批量发送 UDP 数据报(CPU 使用率降低 30%)
import Network
// UDP connection
let connection = NWConnection(
host: NWEndpoint.Host("stream-server.example.com"),
port: NWEndpoint.Port(integerLiteral: 9000),
using: .udp
)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Ready to send UDP")
}
}
connection.start(queue: .main)
// Batch sending for efficiency
func sendVideoFrames(_ frames: [Data]) {
connection.batch {
for frame in frames {
connection.send(content: frame, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
}
}
// All sends batched into ~1 syscall
// 30% lower CPU usage vs individual sends
}
// Receive UDP datagrams
func receiveFrames() {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
// Process video frame
self?.displayFrame(data)
self?.receiveFrames() // Continue receiving
}
}
}
性能特点
- 不使用批量发送 100 个数据报 = 100 次系统调用 = 100 次上下文切换
- 使用批量发送 100 个数据报 = 约 1 次系统调用 = 1 次上下文切换
- 结果 CPU 使用率降低约 30%(使用 Instruments 测得)
适用场景
- 实时视频/音频流
- 频繁更新的游戏(玩家位置)
- 高频传感器数据(IoT)
WWDC 2018 演示 实时视频流演示显示,使用用户空间网络和批量发送后,接收端 CPU 使用率降低了 30%
模式 2c:NWListener(iOS 12-25)
适用场景 需要接受传入连接、构建服务端或点对点应用、支持 iOS 12-25
耗时 20-25 分钟
不推荐:手动监听套接字
// WRONG — Manual socket management
let sock = socket(AF_INET, SOCK_STREAM, 0)
bind(sock, &addr, addrlen)
listen(sock, 5)
while true {
let client = accept(sock, nil, nil) // Blocks thread
// Handle client...
}
推荐:使用 NWListener 自动处理连接
import Network
// Create listener with default parameters
let listener = try NWListener(using: .tcp, on: 1029)
// Advertise Bonjour service
listener.service = NWListener.Service(name: "MyApp", type: "_myservice._tcp")
// Handle service registration updates
listener.serviceRegistrationUpdateHandler = { update in
switch update {
case .add(let endpoint):
if case .service(let name, let type, let domain, _) = endpoint {
print("Advertising as: \(name).\(type)\(domain)")
}
default:
break
}
}
// Handle incoming connections
listener.newConnectionHandler = { [weak self] newConnection in
print("New connection from: \(newConnection.endpoint)")
// Configure connection
newConnection.stateUpdateHandler = { state in
switch state {
case .ready:
print("Client connected")
self?.handleClient(newConnection)
case .failed(let error):
print("Client connection failed: \(error)")
default:
break
}
}
// Start handling this connection
newConnection.start(queue: .main)
}
// Handle listener state
listener.stateUpdateHandler = { state in
switch state {
case .ready:
print("Listener ready on port \(listener.port ?? 0)")
case .failed(let error):
print("Listener failed: \(error)")
default:
break
}
}
// Start listening
listener.start(queue: .main)
// Handle client data
func handleClient(_ connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
print("Received \(data.count) bytes")
// Echo back
connection.send(content: data, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
self?.handleClient(connection) // Continue receiving
}
}
}
适用场景
- 点对点应用(文件共享、消息传递)
- 本地网络服务
- 开发/测试服务端
Bonjour 广播
- 在本地网络上自动发现服务
- 无需硬编码 IP
- 可与 NWBrowser 配合进行发现
安全注意事项
- 使用 TLS 参数进行加密:
NWListener(using: .tls, on: port) - 在处理数据前验证客户端连接
- 设置连接数上限以防止 DoS
模式 2d:网络发现(iOS 12-25)
适用场景 发现本地网络上的服务(Bonjour)、构建点对点应用、支持 iOS 12-25
耗时 25-30 分钟
不推荐:硬编码 IP 地址
// WRONG — Brittle, requires manual configuration
let connection = NWConnection(host: "192.168.1.100", port: 9000, using: .tcp)
// What if IP changes? What if multiple devices?
推荐:使用 NWBrowser 发现服务
import Network
// Browse for services on local network
let browser = NWBrowser(for: .bonjour(type: "_myservice._tcp", domain: nil), using: .tcp)
// Handle discovered services
browser.browseResultsChangedHandler = { results, changes in
for result in results {
switch result.endpoint {
case .service(let name, let type, let domain, _):
print("Found service: \(name).\(type)\(domain)")
// Connect to this service
self.connectToService(result.endpoint)
default:
break
}
}
}
// Handle browser state
browser.stateUpdateHandler = { state in
switch state {
case .ready:
print("Browser ready")
case .failed(let error):
print("Browser failed: \(error)")
default:
break
}
}
// Start browsing
browser.start(queue: .main)
// Connect to discovered service
func connectToService(_ endpoint: NWEndpoint) {
let connection = NWConnection(to: endpoint, using: .tcp)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Connected to service")
}
}
connection.start(queue: .main)
}
适用场景
- 点对点发现(类似 AirDrop 的功能)
- 本地网络打印机、媒体服务端
- 开发/测试(自动查找测试服务端)
性能特点
- 基于 mDNS(多播 DNS,无中央服务端)
- 同一子网内可近乎即时地发现
- 服务出现或消失时自动更新
iOS 26+ 替代方案
- 使用支持 Wi-Fi Aware 的 NetworkBrowser,在无基础设施的情况下实现点对点连接
- 参见 axiom-network-framework-ref skill 中的模式 1d
资源
技能:axiom-networking、axiom-network-framework-ref、axiom-networking-migration
评论
支持 Markdown