最近蘋果開源了Swift版的Nettygit
SwiftNIO是一個跨平臺異步事件驅動的網絡應用程序框架,用於快速開發可維護的高性能協議服務器和客戶端。簡單來講就是能夠用來實現各類高性能服務端和客戶端,如http、tcp。github
由於最近項目恰好要用到udp,因此趁機把日常用Netty實現的該用Swift。bootstrap
首先簡單看一下幾個用到的類swift
顧名思義,這東西是一個線程池。每一個EventLoopGroup
裏面有多個EventLoop
,而每一個EventLoop
都與一個線程綁定。promise
Bootstrap
是開發Netty程序的基礎,SwiftNIO也是同樣。經過Bootstrap
的bind方法來建立鏈接,咱們也能夠經過該方法返回的Channel
來判斷是否建立成功。服務器
ChannelHandler
是用來處理數據,如客戶端向服務端發送數據,服務端的數據處理就是在ChannelHandler
中完成。ChannelHandler
自己是一個protocol,咱們用到的有ChannelInboundHandler
和ChannelOutboundHandler
這兩個,ChannlPipeline
會從頭至尾順序調用ChannelInboundHandler
處理數據,從尾到頭調用ChannelOutboundHandler
數據。網絡
接下來看服務端代碼:app
class UDPServer {
private let loopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
func listen(on port: Int) {
let bootstrap = DatagramBootstrap(group: loopGroup)
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.channelInitializer { (channel) -> EventLoopFuture<Void> in
channel.pipeline.add(handler: UDPServerHandler())
}
do {
let channel = try bootstrap.bind(host: "127.0.0.1", port: port).wait()
print("listen on \(channel.localAddress!)")
try channel.closeFuture.wait()
} catch {
print(error)
}
}
final class UDPServerHandler: ChannelInboundHandler {
typealias InboundIn = AddressedEnvelope<ByteBuffer>
typealias OutboundOut = AddressedEnvelope<ByteBuffer>
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
let getData = unwrapInboundIn(data)
print(getData.data)
}
}
}
let server = UDPServer()
server.listen(on: 5656)
複製代碼
運行代碼就會能夠看到控制檯輸出: listen on [IPv4]127.0.0.1:5656
框架
咱們經過內部類的形式實現了一個 ChannelInboundHandler
,並把它添加到 ChannlPipeline
。Handler
須要設置兩個東西 InboundIn
和 OutboundOut
。異步
InboundIn
: 是入站數據的類型,就是接收客戶端發過來的數據類型。 OutboundOut
: 是出站數據的類型,就是返回給客戶端的數據類型,同時也是傳遞給下一個ChannelOutboundHandler
的類型。
咱們這裏用的是 AddressedEnvelope<ByteBuffer>
它裏面是一個 SocketAddress
加上 ByteBuffer
。
當客戶端發來數據的時候會調用Handler
的channelRead(ctx: , data: )
這裏進來的是NIOAny
類型,須要調用 Handler
的 unwrapInboundIn()
方法把 NIOAny
轉成 InboundIn
類型。
就這樣一個簡單的UDP服務端就完成了,能夠經過各類UDP工具進行測試,或者用SwiftNIO再寫一個客戶端:
class UDPClient {
private let loopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
private var channel: Channel!
init(port: Int) {
let bootstrap = DatagramBootstrap(group: loopGroup)
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.channelInitializer { (channel) -> EventLoopFuture<Void> in
channel.pipeline.add(handler: UDPClientHandler())
}
do {
channel = try bootstrap.bind(host: "0.0.0.0", port: port).wait()
sent(with: "test".data(using: .utf8)!)
} catch {
print(error)
}
}
func sent(with data: Data) {
var byteBuffer = ByteBufferAllocator().buffer(capacity: data.count)
byteBuffer.write(bytes: data)
let address = try! SocketAddress(ipAddress: "127.0.0.1", port: 5656)
channel.writeAndFlush(NIOAny(AddressedEnvelope<ByteBuffer>(remoteAddress: address, data: byteBuffer)), promise: nil)
}
final class UDPClientHandler: ChannelInboundHandler {
typealias InboundIn = AddressedEnvelope<ByteBuffer>
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
print(data)
}
}
}
let client = UDPClient(port: 22222)
複製代碼
到這裏,用SwiftNIO構建簡單的UDP通信已經OK了。