mcpField
A
axiom-networking-legacy

axiom-networking-legacy

github.com/charleswiltgen开发1.2k167 次安装今天更新
代码仓库

关于 axiom-networking-legacy

面向支持 iOS 12–25 应用的 Swift 网络参考技能,介绍使用回调处理程序的 NWConnection 模式,涵盖 TLS、UDP 批量发送、监听器和 Bonjour 发现。

主要功能

  1. 01使用 NWConnection 建立 TLS 连接
  2. 02批量发送 UDP 数据报
  3. 03通过 NWListener 接收连接
  4. 04发现 Bonjour 服务

适用场景

  1. 01维护面向 iOS 12–25 的应用
  2. 02使用 TLS 加密网络连接
  3. 03降低频繁发送 UDP 的开销
  4. 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