swift-nio

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Swift NIO

Swift NIO

Overview

概述

This skill provides expert guidance on SwiftNIO, Apple's event-driven network application framework. Use this skill to help developers write safe, performant networking code, build protocol implementations, and properly integrate with Swift Concurrency.
本技能提供关于SwiftNIO(苹果的事件驱动网络应用框架)的专业指导。使用本技能可帮助开发者编写安全、高性能的网络代码,构建协议实现,并与Swift并发模型正确集成。

Agent Behavior Contract (Follow These Rules)

Agent行为约定(请遵循以下规则)

  1. Analyze the project's
    Package.swift
    to determine which SwiftNIO packages are used.
  2. Before proposing fixes, identify if Swift Concurrency can be used instead of
    EventLoopFuture
    chains.
  3. Never recommend blocking the EventLoop - this is the most critical rule in SwiftNIO development.
  4. Prefer
    NIOAsyncChannel
    and structured concurrency over legacy
    ChannelHandler
    patterns for new code.
  5. Use
    EventLoopFuture
    /
    EventLoopPromise
    only for low-level protocol implementations.
  6. When working with
    ByteBuffer
    , always consider memory ownership and avoid unnecessary copies.
  1. 分析项目的
    Package.swift
    以确定使用了哪些SwiftNIO包。
  2. 在提出修复方案前,判断是否可以使用Swift并发替代
    EventLoopFuture
    链式调用。
  3. 绝对不要推荐阻塞EventLoop——这是SwiftNIO开发中最关键的规则。
  4. 对于新代码,优先使用
    NIOAsyncChannel
    和结构化并发,而非传统的
    ChannelHandler
    模式。
  5. 仅在底层协议实现中使用
    EventLoopFuture
    /
    EventLoopPromise
  6. 处理
    ByteBuffer
    时,始终考虑内存所有权,避免不必要的复制。

Quick Decision Tree

快速决策树

When a developer needs SwiftNIO guidance, follow this decision tree:
  1. Building a TCP/UDP server or client?
    • Read
      references/Channels.md
      for Channel concepts and
      NIOAsyncChannel
    • Use
      ServerBootstrap
      for TCP servers,
      DatagramBootstrap
      for UDP
  2. Understanding EventLoops?
    • Read
      references/EventLoops.md
      for event loop concepts
    • Critical: Never block the EventLoop!
  3. Working with binary data?
    • Read
      references/ByteBuffer.md
      for buffer operations
    • Prefer slice views over copies when possible
  4. Implementing a binary protocol?
    • Read
      references/ByteToMessageCodecs.md
      for codec patterns
    • Use
      ByteToMessageDecoder
      and
      MessageToByteEncoder
  5. Migrating from EventLoopFuture to async/await?
    • Use
      .get()
      to bridge futures to async
    • Use
      NIOAsyncChannel
      for channel-based async code
当开发者需要SwiftNIO相关指导时,请遵循以下决策树:
  1. 是否要构建TCP/UDP服务器或客户端?
    • 阅读
      references/Channels.md
      了解Channel概念和
      NIOAsyncChannel
    • TCP服务器使用
      ServerBootstrap
      ,UDP使用
      DatagramBootstrap
  2. 是否要理解EventLoops?
    • 阅读
      references/EventLoops.md
      了解事件循环概念
    • 关键:绝对不要阻塞EventLoop!
  3. 是否要处理二进制数据?
    • 阅读
      references/ByteBuffer.md
      了解缓冲区操作
    • 尽可能优先使用切片视图而非复制
  4. 是否要实现二进制协议?
    • 阅读
      references/ByteToMessageCodecs.md
      了解编解码器模式
    • 使用
      ByteToMessageDecoder
      MessageToByteEncoder
  5. 是否要从EventLoopFuture迁移到async/await?
    • 使用
      .get()
      将futures桥接到异步代码
    • 基于Channel的异步代码使用
      NIOAsyncChannel

Triage-First Playbook (Common Issues -> Solutions)

优先排查手册(常见问题 -> 解决方案)

  • "Blocking the EventLoop"
    • Offload CPU-intensive work to a dispatch queue or use
      NIOThreadPool
    • Never perform synchronous I/O on an EventLoop
    • See
      references/EventLoops.md
  • Type mismatch crash in ChannelPipeline
    • Ensure
      InboundOut
      of handler N matches
      InboundIn
      of handler N+1
    • Ensure
      OutboundOut
      of handler N matches
      OutboundIn
      of handler N-1
    • See
      references/Channels.md
  • Implementing binary protocol serialization
    • Use
      ByteToMessageDecoder
      for parsing bytes into messages
    • Use
      MessageToByteEncoder
      for serializing messages to bytes
    • Use
      readLengthPrefixedSlice
      and
      writeLengthPrefixed
      helpers
    • See
      references/ByteToMessageCodecs.md
  • Memory issues with ByteBuffer
    • Use
      readSlice
      instead of
      readBytes
      when possible
    • Remember ByteBuffer uses copy-on-write semantics
    • See
      references/ByteBuffer.md
  • Deadlock when waiting for EventLoopFuture
    • Never
      .wait()
      on a future from within the same EventLoop
    • Use
      .get()
      from async contexts or chain with
      .flatMap
  • "阻塞EventLoop"
    • 将CPU密集型工作卸载到调度队列或使用
      NIOThreadPool
    • 绝对不要在EventLoop上执行同步I/O
    • 参考
      references/EventLoops.md
  • ChannelPipeline中的类型不匹配崩溃
    • 确保处理器N的
      InboundOut
      与处理器N+1的
      InboundIn
      匹配
    • 确保处理器N的
      OutboundOut
      与处理器N-1的
      OutboundIn
      匹配
    • 参考
      references/Channels.md
  • 实现二进制协议序列化
    • 使用
      ByteToMessageDecoder
      将字节解析为消息
    • 使用
      MessageToByteEncoder
      将消息序列化为字节
    • 使用
      readLengthPrefixedSlice
      writeLengthPrefixed
      辅助方法
    • 参考
      references/ByteToMessageCodecs.md
  • ByteBuffer内存问题
    • 尽可能使用
      readSlice
      而非
      readBytes
    • 记住ByteBuffer使用写时复制语义
    • 参考
      references/ByteBuffer.md
  • 等待EventLoopFuture时出现死锁
    • 绝对不要在同一个EventLoop内对future调用
      .wait()
    • 在异步上下文中使用
      .get()
      或用
      .flatMap
      链式调用

Core Patterns Reference

核心模式参考

Creating a TCP Server (Modern Approach)

创建TCP服务器(现代方式)

swift
let server = try await ServerBootstrap(group: MultiThreadedEventLoopGroup.singleton)
    .bind(host: "0.0.0.0", port: 8080) { channel in
        channel.eventLoop.makeCompletedFuture {
            try NIOAsyncChannel(
                wrappingChannelSynchronously: channel,
                configuration: .init(
                    inboundType: ByteBuffer.self,
                    outboundType: ByteBuffer.self
                )
            )
        }
    }

try await withThrowingDiscardingTaskGroup { group in
    try await server.executeThenClose { clients in
        for try await client in clients {
            group.addTask {
                try await handleClient(client)
            }
        }
    }
}
swift
let server = try await ServerBootstrap(group: MultiThreadedEventLoopGroup.singleton)
    .bind(host: "0.0.0.0", port: 8080) { channel in
        channel.eventLoop.makeCompletedFuture {
            try NIOAsyncChannel(
                wrappingChannelSynchronously: channel,
                configuration: .init(
                    inboundType: ByteBuffer.self,
                    outboundType: ByteBuffer.self
                )
            )
        }
    }

try await withThrowingDiscardingTaskGroup { group in
    try await server.executeThenClose { clients in
        for try await client in clients {
            group.addTask {
                try await handleClient(client)
            }
        }
    }
}

EventLoopGroup Best Practice

EventLoopGroup最佳实践

swift
// Preferred: Use the singleton
let group = MultiThreadedEventLoopGroup.singleton

// Get any EventLoop from the group
let eventLoop = group.any()
swift
// 推荐:使用单例
let group = MultiThreadedEventLoopGroup.singleton

// 从组中获取任意EventLoop
let eventLoop = group.any()

Bridging EventLoopFuture to async/await

桥接EventLoopFuture到async/await

swift
// From EventLoopFuture to async
let result = try await someFuture.get()

// From async to EventLoopFuture
let future = eventLoop.makeFutureWithTask {
    try await someAsyncOperation()
}
swift
// 从EventLoopFuture转换到异步
let result = try await someFuture.get()

// 从异步转换到EventLoopFuture
let future = eventLoop.makeFutureWithTask {
    try await someAsyncOperation()
}

ByteBuffer Operations

ByteBuffer操作

swift
var buffer = ByteBufferAllocator().buffer(capacity: 1024)

// Writing
buffer.writeString("Hello")
buffer.writeInteger(UInt32(42))

// Reading
let string = buffer.readString(length: 5)
let number = buffer.readInteger(as: UInt32.self)
swift
var buffer = ByteBufferAllocator().buffer(capacity: 1024)

// 写入
buffer.writeString("Hello")
buffer.writeInteger(UInt32(42))

// 读取
let string = buffer.readString(length: 5)
let number = buffer.readInteger(as: UInt32.self)

Reference Files

参考文档

Load these files as needed for specific topics:
  • EventLoops.md
    - EventLoop concepts, nonblocking I/O, why blocking is bad
  • Channels.md
    - Channel anatomy, ChannelPipeline, ChannelHandlers, NIOAsyncChannel
  • ByteToMessageCodecs.md
    - ByteToMessageDecoder, MessageToByteEncoder for binary protocol (de)serialization
  • patterns.md
    - Advanced integration patterns: ServerChildChannel abstraction, state machines, noncopyable ResponseWriter, graceful shutdown, ByteBuffer patterns
根据特定主题按需加载以下文档:
  • EventLoops.md
    - EventLoop概念、非阻塞I/O、阻塞的危害
  • Channels.md
    - Channel结构、ChannelPipeline、ChannelHandlers、NIOAsyncChannel
  • ByteToMessageCodecs.md
    - 用于二进制协议(反)序列化的ByteToMessageDecoder、MessageToByteEncoder
  • patterns.md
    - 高级集成模式:ServerChildChannel抽象、状态机、非可复制ResponseWriter、优雅关闭、ByteBuffer模式

Best Practices Summary

最佳实践总结

  1. Never block the EventLoop - Offload heavy work to thread pools
  2. Use structured concurrency - Prefer
    NIOAsyncChannel
    over legacy handlers
  3. Use the singleton EventLoopGroup -
    MultiThreadedEventLoopGroup.singleton
  4. Handle errors in task groups - Throwing from a client task closes the server
  5. Mind the types in pipelines - Type mismatches crash at runtime
  6. Use ByteBuffer efficiently - Prefer slices over copies
  1. 绝对不要阻塞EventLoop - 将繁重工作卸载到线程池
  2. 使用结构化并发 - 优先使用
    NIOAsyncChannel
    而非传统处理器
  3. 使用单例EventLoopGroup -
    MultiThreadedEventLoopGroup.singleton
  4. 在任务组中处理错误 - 客户端任务抛出异常会关闭服务器
  5. 注意管道中的类型 - 类型不匹配会导致运行时崩溃
  6. 高效使用ByteBuffer - 优先使用切片而非复制

Use ByteBuffer for Binary Protocol Handling

使用ByteBuffer处理二进制协议

When parsing or serializing binary data (especially for network protocols), use SwiftNIO's
ByteBuffer
instead of Foundation's
Data
. ByteBuffer provides:
  • Efficient read/write operations with built-in endianness handling
  • Zero-copy slicing with reader/writer index tracking
  • Integration with NIO ecosystem
When converting between ByteBuffer and Data, use
NIOFoundationCompat
:
swift
import NIOFoundationCompat

// ByteBuffer to Data
let data = Data(buffer: byteBuffer)

// Data to ByteBuffer - use writeData for better performance
var buffer = ByteBuffer()
buffer.writeData(data)  // Faster than writeBytes(data)
Bad - Using Data with manual byte manipulation:
swift
var buffer = Data()
var messageLength: UInt32?

for try await message in inbound {
    buffer.append(message)

    if messageLength == nil && buffer.count >= 4 {
        messageLength = UInt32(buffer[0]) << 24
            | UInt32(buffer[1]) << 16
            | UInt32(buffer[2]) << 8
            | UInt32(buffer[3])
        buffer = Data(buffer.dropFirst(4))  // Copies data!
    }
}
Good - Using ByteBuffer:
swift
var buffer = ByteBuffer()

for try await message in inbound {
    buffer.writeBytes(message)

    if buffer.readableBytes >= 4 {
        let readerIndex = buffer.readerIndex
        guard let messageLength = buffer.readInteger(endianness: .big, as: UInt32.self) else {
            continue
        }

        if buffer.readableBytes >= messageLength {
            guard let bytes = buffer.readBytes(length: Int(messageLength)) else { continue }
            // Process bytes...
        } else {
            // Not enough data yet, reset reader index
            buffer.moveReaderIndex(to: readerIndex)
        }
    }
}
解析或序列化二进制数据(尤其是网络协议)时,使用SwiftNIO的
ByteBuffer
而非Foundation的
Data
。ByteBuffer提供:
  • 内置字节序处理的高效读写操作
  • 带读写索引跟踪的零拷贝切片
  • 与NIO生态系统的集成
在ByteBuffer和Data之间转换时,使用
NIOFoundationCompat
swift
import NIOFoundationCompat

// ByteBuffer转Data
let data = Data(buffer: byteBuffer)

// Data转ByteBuffer - 使用writeData性能更优
var buffer = ByteBuffer()
buffer.writeData(data)  // 比writeBytes(data)更快
不推荐 - 使用Data手动操作字节:
swift
var buffer = Data()
var messageLength: UInt32?

for try await message in inbound {
    buffer.append(message)

    if messageLength == nil && buffer.count >= 4 {
        messageLength = UInt32(buffer[0]) << 24
            | UInt32(buffer[1]) << 16
            | UInt32(buffer[2]) << 8
            | UInt32(buffer[3])
        buffer = Data(buffer.dropFirst(4))  // 会复制数据!
    }
}
推荐 - 使用ByteBuffer:
swift
var buffer = ByteBuffer()

for try await message in inbound {
    buffer.writeBytes(message)

    if buffer.readableBytes >= 4 {
        let readerIndex = buffer.readerIndex
        guard let messageLength = buffer.readInteger(endianness: .big, as: UInt32.self) else {
            continue
        }

        if buffer.readableBytes >= messageLength {
            guard let bytes = buffer.readBytes(length: Int(messageLength)) else { continue }
            // 处理字节...
        } else {
            // 数据不足,重置读取索引
            buffer.moveReaderIndex(to: readerIndex)
        }
    }
}

Binary Data Types Comparison

二进制数据类型对比

There are several "bag of bytes" data structures in Swift:
TypeSourcePlatformNotes
Array<UInt8>
stdlibAllSafe, growable, good for Embedded Swift
InlineArray<N, UInt8>
stdlib (6.1+)AllFixed-size, stack-allocated, no heap allocation
Data
FoundationAll (large binary)Not always contiguous on Apple platforms
ByteBuffer
SwiftNIOAll (requires NIO)Best for network protocols, not Embedded
Span<UInt8>
stdlib (6.2+)AllZero-copy view, requires Swift 6.2+
UnsafeBufferPointer<UInt8>
stdlibAllUnsafe, manual memory management
Recommendations:
  • For iOS/macOS-only projects:
    Data
    is fine due to framework integration
  • For SwiftNIO-based projects:
    ByteBuffer
    is required for I/O operations
  • For Embedded Swift:
    [UInt8]
    and
    InlineArray
  • For cross-platform APIs:
    Span<UInt8>
    (Swift 6.2+) allows any backing type
Swift中有几种“字节容器”数据结构:
类型来源平台说明
Array<UInt8>
标准库所有安全、可扩容,适用于嵌入式Swift
InlineArray<N, UInt8>
标准库(6.1+)所有固定大小、栈分配,无堆内存分配
Data
Foundation所有(大二进制数据)在苹果平台上不总是连续内存
ByteBuffer
SwiftNIO所有(需要NIO)最适合网络协议,不适用于嵌入式Swift
Span<UInt8>
标准库(6.2+)所有零拷贝视图,需要Swift 6.2+
UnsafeBufferPointer<UInt8>
标准库所有不安全,需手动管理内存
推荐方案:
  • 仅iOS/macOS项目:
    Data
    即可,因框架集成度高
  • SwiftNIO项目:
    ByteBuffer
    是I/O操作的必需类型
  • 嵌入式Swift:
    [UInt8]
    InlineArray
  • 跨平台API:
    Span<UInt8>
    (Swift 6.2+)支持任意底层类型

NIO Channel Pattern with executeThenClose

带executeThenClose的NIO Channel模式

Use
executeThenClose
to get inbound/outbound streams from NIOAsyncChannel:
swift
return try await channel.executeThenClose { inbound, outbound in
    let socket = Client(inbound: inbound, outbound: outbound, channel: channel.channel)
    return try await perform(client)
}
使用
executeThenClose
从NIOAsyncChannel获取入站/出站流:
swift
return try await channel.executeThenClose { inbound, outbound in
    let socket = Client(inbound: inbound, outbound: outbound, channel: channel.channel)
    return try await perform(client)
}

Public API with Internal NIO Types

包含内部NIO类型的公开API

When exposing async sequences that wrap NIO types:
  1. Create a custom
    AsyncSequence
    wrapper struct with internal NIO stream
  2. The wrapper's
    AsyncIterator
    transforms NIO types to public types
  3. This avoids exposing internal NIO imports in public API
暴露包装NIO类型的异步序列时:
  1. 创建自定义
    AsyncSequence
    包装结构体,内部包含NIO流
  2. 包装器的
    AsyncIterator
    将NIO类型转换为公开类型
  3. 避免在公开API中暴露内部NIO导入

SwiftNIO UDP Notes

SwiftNIO UDP注意事项

  • Use
    DatagramBootstrap
    for UDP sockets
  • Messages use
    AddressedEnvelope<ByteBuffer>
    containing remote address and data
  • Multicast requires casting channel to
    MulticastChannel
    protocol
  • Socket options use
    SocketOptionValue
    (Int32) type
  • so_reuseport
    is only available on Linux
  • 使用
    DatagramBootstrap
    创建UDP套接字
  • 消息使用
    AddressedEnvelope<ByteBuffer>
    ,包含远程地址和数据
  • 多播需将channel转换为
    MulticastChannel
    协议
  • 套接字选项使用
    SocketOptionValue
    (Int32)类型
  • so_reuseport
    仅在Linux上可用