SwiftNIO初探-簡單UDP通信

最近蘋果開源了Swift版的Nettygit

SwiftNIO是一個跨平臺異步事件驅動的網絡應用程序框架,用於快速開發可維護的高性能協議服務器和客戶端。簡單來講就是能夠用來實現各類高性能服務端和客戶端,如http、tcp。github

由於最近項目恰好要用到udp,因此趁機把日常用Netty實現的該用Swift。bootstrap

首先簡單看一下幾個用到的類swift

MultiThreadedEventLoopGroup

顧名思義,這東西是一個線程池。每一個EventLoopGroup裏面有多個EventLoop,而每一個EventLoop都與一個線程綁定。promise

DatagramBootstrap

Bootstrap是開發Netty程序的基礎,SwiftNIO也是同樣。經過Bootstrap的bind方法來建立鏈接,咱們也能夠經過該方法返回的Channel來判斷是否建立成功。服務器

ChannelHandler

ChannelHandler 是用來處理數據,如客戶端向服務端發送數據,服務端的數據處理就是在ChannelHandler中完成。ChannelHandler 自己是一個protocol,咱們用到的有ChannelInboundHandlerChannelOutboundHandler這兩個,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 ,並把它添加到 ChannlPipelineHandler 須要設置兩個東西 InboundInOutboundOut異步

InboundIn: 是入站數據的類型,就是接收客戶端發過來的數據類型。 OutboundOut: 是出站數據的類型,就是返回給客戶端的數據類型,同時也是傳遞給下一個ChannelOutboundHandler的類型。

咱們這裏用的是 AddressedEnvelope<ByteBuffer> 它裏面是一個 SocketAddress 加上 ByteBuffer

當客戶端發來數據的時候會調用HandlerchannelRead(ctx: , data: ) 這裏進來的是NIOAny類型,須要調用 HandlerunwrapInboundIn() 方法把 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了。

相關文章
相關標籤/搜索