swift-nio
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwift 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行为约定(请遵循以下规则)
- Analyze the project's to determine which SwiftNIO packages are used.
Package.swift - Before proposing fixes, identify if Swift Concurrency can be used instead of chains.
EventLoopFuture - Never recommend blocking the EventLoop - this is the most critical rule in SwiftNIO development.
- Prefer and structured concurrency over legacy
NIOAsyncChannelpatterns for new code.ChannelHandler - Use /
EventLoopFutureonly for low-level protocol implementations.EventLoopPromise - When working with , always consider memory ownership and avoid unnecessary copies.
ByteBuffer
- 分析项目的以确定使用了哪些SwiftNIO包。
Package.swift - 在提出修复方案前,判断是否可以使用Swift并发替代链式调用。
EventLoopFuture - 绝对不要推荐阻塞EventLoop——这是SwiftNIO开发中最关键的规则。
- 对于新代码,优先使用和结构化并发,而非传统的
NIOAsyncChannel模式。ChannelHandler - 仅在底层协议实现中使用/
EventLoopFuture。EventLoopPromise - 处理时,始终考虑内存所有权,避免不必要的复制。
ByteBuffer
Quick Decision Tree
快速决策树
When a developer needs SwiftNIO guidance, follow this decision tree:
-
Building a TCP/UDP server or client?
- Read for Channel concepts and
references/Channels.mdNIOAsyncChannel - Use for TCP servers,
ServerBootstrapfor UDPDatagramBootstrap
- Read
-
Understanding EventLoops?
- Read for event loop concepts
references/EventLoops.md - Critical: Never block the EventLoop!
- Read
-
Working with binary data?
- Read for buffer operations
references/ByteBuffer.md - Prefer slice views over copies when possible
- Read
-
Implementing a binary protocol?
- Read for codec patterns
references/ByteToMessageCodecs.md - Use and
ByteToMessageDecoderMessageToByteEncoder
- Read
-
Migrating from EventLoopFuture to async/await?
- Use to bridge futures to async
.get() - Use for channel-based async code
NIOAsyncChannel
- Use
当开发者需要SwiftNIO相关指导时,请遵循以下决策树:
-
是否要构建TCP/UDP服务器或客户端?
- 阅读了解Channel概念和
references/Channels.mdNIOAsyncChannel - TCP服务器使用,UDP使用
ServerBootstrapDatagramBootstrap
- 阅读
-
是否要理解EventLoops?
- 阅读了解事件循环概念
references/EventLoops.md - 关键:绝对不要阻塞EventLoop!
- 阅读
-
是否要处理二进制数据?
- 阅读了解缓冲区操作
references/ByteBuffer.md - 尽可能优先使用切片视图而非复制
- 阅读
-
是否要实现二进制协议?
- 阅读了解编解码器模式
references/ByteToMessageCodecs.md - 使用和
ByteToMessageDecoderMessageToByteEncoder
- 阅读
-
是否要从EventLoopFuture迁移到async/await?
- 使用将futures桥接到异步代码
.get() - 基于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
- Offload CPU-intensive work to a dispatch queue or use
-
Type mismatch crash in ChannelPipeline
- Ensure of handler N matches
InboundOutof handler N+1InboundIn - Ensure of handler N matches
OutboundOutof handler N-1OutboundIn - See
references/Channels.md
- Ensure
-
Implementing binary protocol serialization
- Use for parsing bytes into messages
ByteToMessageDecoder - Use for serializing messages to bytes
MessageToByteEncoder - Use and
readLengthPrefixedSlicehelperswriteLengthPrefixed - See
references/ByteToMessageCodecs.md
- Use
-
Memory issues with ByteBuffer
- Use instead of
readSlicewhen possiblereadBytes - Remember ByteBuffer uses copy-on-write semantics
- See
references/ByteBuffer.md
- Use
-
Deadlock when waiting for EventLoopFuture
- Never on a future from within the same EventLoop
.wait() - Use from async contexts or chain with
.get().flatMap
- Never
-
"阻塞EventLoop"
- 将CPU密集型工作卸载到调度队列或使用
NIOThreadPool - 绝对不要在EventLoop上执行同步I/O
- 参考
references/EventLoops.md
- 将CPU密集型工作卸载到调度队列或使用
-
ChannelPipeline中的类型不匹配崩溃
- 确保处理器N的与处理器N+1的
InboundOut匹配InboundIn - 确保处理器N的与处理器N-1的
OutboundOut匹配OutboundIn - 参考
references/Channels.md
- 确保处理器N的
-
实现二进制协议序列化
- 使用将字节解析为消息
ByteToMessageDecoder - 使用将消息序列化为字节
MessageToByteEncoder - 使用和
readLengthPrefixedSlice辅助方法writeLengthPrefixed - 参考
references/ByteToMessageCodecs.md
- 使用
-
ByteBuffer内存问题
- 尽可能使用而非
readSlicereadBytes - 记住ByteBuffer使用写时复制语义
- 参考
references/ByteBuffer.md
- 尽可能使用
-
等待EventLoopFuture时出现死锁
- 绝对不要在同一个EventLoop内对future调用
.wait() - 在异步上下文中使用或用
.get()链式调用.flatMap
- 绝对不要在同一个EventLoop内对future调用
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:
- - EventLoop concepts, nonblocking I/O, why blocking is bad
EventLoops.md - - Channel anatomy, ChannelPipeline, ChannelHandlers, NIOAsyncChannel
Channels.md - - ByteToMessageDecoder, MessageToByteEncoder for binary protocol (de)serialization
ByteToMessageCodecs.md - - Advanced integration patterns: ServerChildChannel abstraction, state machines, noncopyable ResponseWriter, graceful shutdown, ByteBuffer patterns
patterns.md
根据特定主题按需加载以下文档:
- - EventLoop概念、非阻塞I/O、阻塞的危害
EventLoops.md - - Channel结构、ChannelPipeline、ChannelHandlers、NIOAsyncChannel
Channels.md - - 用于二进制协议(反)序列化的ByteToMessageDecoder、MessageToByteEncoder
ByteToMessageCodecs.md - - 高级集成模式:ServerChildChannel抽象、状态机、非可复制ResponseWriter、优雅关闭、ByteBuffer模式
patterns.md
Best Practices Summary
最佳实践总结
- Never block the EventLoop - Offload heavy work to thread pools
- Use structured concurrency - Prefer over legacy handlers
NIOAsyncChannel - Use the singleton EventLoopGroup -
MultiThreadedEventLoopGroup.singleton - Handle errors in task groups - Throwing from a client task closes the server
- Mind the types in pipelines - Type mismatches crash at runtime
- Use ByteBuffer efficiently - Prefer slices over copies
- 绝对不要阻塞EventLoop - 将繁重工作卸载到线程池
- 使用结构化并发 - 优先使用而非传统处理器
NIOAsyncChannel - 使用单例EventLoopGroup -
MultiThreadedEventLoopGroup.singleton - 在任务组中处理错误 - 客户端任务抛出异常会关闭服务器
- 注意管道中的类型 - 类型不匹配会导致运行时崩溃
- 高效使用ByteBuffer - 优先使用切片而非复制
Use ByteBuffer for Binary Protocol Handling
使用ByteBuffer处理二进制协议
When parsing or serializing binary data (especially for network protocols), use SwiftNIO's instead of Foundation's . ByteBuffer provides:
ByteBufferData- 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 :
NIOFoundationCompatswift
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的而非Foundation的。ByteBuffer提供:
ByteBufferData- 内置字节序处理的高效读写操作
- 带读写索引跟踪的零拷贝切片
- 与NIO生态系统的集成
在ByteBuffer和Data之间转换时,使用:
NIOFoundationCompatswift
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:
| Type | Source | Platform | Notes |
|---|---|---|---|
| stdlib | All | Safe, growable, good for Embedded Swift |
| stdlib (6.1+) | All | Fixed-size, stack-allocated, no heap allocation |
| Foundation | All (large binary) | Not always contiguous on Apple platforms |
| SwiftNIO | All (requires NIO) | Best for network protocols, not Embedded |
| stdlib (6.2+) | All | Zero-copy view, requires Swift 6.2+ |
| stdlib | All | Unsafe, manual memory management |
Recommendations:
- For iOS/macOS-only projects: is fine due to framework integration
Data - For SwiftNIO-based projects: is required for I/O operations
ByteBuffer - For Embedded Swift: and
[UInt8]InlineArray - For cross-platform APIs: (Swift 6.2+) allows any backing type
Span<UInt8>
Swift中有几种“字节容器”数据结构:
| 类型 | 来源 | 平台 | 说明 |
|---|---|---|---|
| 标准库 | 所有 | 安全、可扩容,适用于嵌入式Swift |
| 标准库(6.1+) | 所有 | 固定大小、栈分配,无堆内存分配 |
| Foundation | 所有(大二进制数据) | 在苹果平台上不总是连续内存 |
| SwiftNIO | 所有(需要NIO) | 最适合网络协议,不适用于嵌入式Swift |
| 标准库(6.2+) | 所有 | 零拷贝视图,需要Swift 6.2+ |
| 标准库 | 所有 | 不安全,需手动管理内存 |
推荐方案:
- 仅iOS/macOS项目:即可,因框架集成度高
Data - SwiftNIO项目:是I/O操作的必需类型
ByteBuffer - 嵌入式Swift:和
[UInt8]InlineArray - 跨平台API:(Swift 6.2+)支持任意底层类型
Span<UInt8>
NIO Channel Pattern with executeThenClose
带executeThenClose的NIO Channel模式
Use to get inbound/outbound streams from NIOAsyncChannel:
executeThenCloseswift
return try await channel.executeThenClose { inbound, outbound in
let socket = Client(inbound: inbound, outbound: outbound, channel: channel.channel)
return try await perform(client)
}使用从NIOAsyncChannel获取入站/出站流:
executeThenCloseswift
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:
- Create a custom wrapper struct with internal NIO stream
AsyncSequence - The wrapper's transforms NIO types to public types
AsyncIterator - This avoids exposing internal NIO imports in public API
暴露包装NIO类型的异步序列时:
- 创建自定义包装结构体,内部包含NIO流
AsyncSequence - 包装器的将NIO类型转换为公开类型
AsyncIterator - 避免在公开API中暴露内部NIO导入
SwiftNIO UDP Notes
SwiftNIO UDP注意事项
- Use for UDP sockets
DatagramBootstrap - Messages use containing remote address and data
AddressedEnvelope<ByteBuffer> - Multicast requires casting channel to protocol
MulticastChannel - Socket options use (Int32) type
SocketOptionValue - is only available on Linux
so_reuseport
- 使用创建UDP套接字
DatagramBootstrap - 消息使用,包含远程地址和数据
AddressedEnvelope<ByteBuffer> - 多播需将channel转换为协议
MulticastChannel - 套接字选项使用(Int32)类型
SocketOptionValue - 仅在Linux上可用
so_reuseport