最近蘋果開源了Swift版的Netty
SwiftNIO是一個跨平臺異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用程序框架雕什,用于快速開發(fā)可維護(hù)的高性能協(xié)議服務(wù)器和客戶端缠俺。簡單來說就是可以用來實(shí)現(xiàn)各種高性能服務(wù)端和客戶端,如http贷岸、tcp壹士。
因?yàn)樽罱?xiàng)目剛好要用到udp,所以趁機(jī)把平常用Netty實(shí)現(xiàn)的該用Swift偿警。
首先簡單看一下幾個用到的類
MultiThreadedEventLoopGroup
顧名思義躏救,這東西是一個線程池。每個EventLoopGroup
里面有多個EventLoop
螟蒸,而每個EventLoop
都與一個線程綁定盒使。
DatagramBootstrap
Bootstrap
是開發(fā)Netty程序的基礎(chǔ),SwiftNIO也是一樣七嫌。通過Bootstrap
的bind方法來創(chuàng)建連接少办,我們也可以通過該方法返回的Channel
來判斷是否創(chuàng)建成功。
ChannelHandler
ChannelHandler
是用來處理數(shù)據(jù)诵原,如客戶端向服務(wù)端發(fā)送數(shù)據(jù)英妓,服務(wù)端的數(shù)據(jù)處理就是在ChannelHandler
中完成。ChannelHandler
本身是一個protocol绍赛,我們用到的有ChannelInboundHandler
和ChannelOutboundHandler
這兩個蔓纠,ChannlPipeline
會從頭到尾順序調(diào)用ChannelInboundHandler
處理數(shù)據(jù),從尾到頭調(diào)用ChannelOutboundHandler
數(shù)據(jù)吗蚌。
接下來看服務(wù)端代碼:
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)
運(yùn)行代碼就會可以看到控制臺輸出:
listen on [IPv4]127.0.0.1:5656
我們通過內(nèi)部類的形式實(shí)現(xiàn)了一個 ChannelInboundHandler
腿倚,并把它添加到
ChannlPipeline
。Handler
需要設(shè)置兩個東西 InboundIn
和 OutboundOut
蚯妇。
InboundIn
: 是入站數(shù)據(jù)的類型猴誊,就是接收客戶端發(fā)過來的數(shù)據(jù)類型。
OutboundOut
: 是出站數(shù)據(jù)的類型侮措,就是返回給客戶端的數(shù)據(jù)類型,同時也是傳遞給下一個ChannelOutboundHandler
的類型乖杠。
我們這里用的是 AddressedEnvelope<ByteBuffer>
它里面是一個 SocketAddress
加上 ByteBuffer
分扎。
當(dāng)客戶端發(fā)來數(shù)據(jù)的時候會調(diào)用Handler
的channelRead(ctx: , data: )
這里進(jìn)來的是NIOAny
類型,需要調(diào)用 Handler
的 unwrapInboundIn()
方法把 NIOAny
轉(zhuǎn)成 InboundIn
類型胧洒。
就這樣一個簡單的UDP服務(wù)端就完成了畏吓,可以通過各種UDP工具進(jìn)行測試,或者用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構(gòu)建簡單的UDP通訊已經(jīng)OK了菲饼。