首页 > 其他分享 >ios 之 netty版本swiftNio(DNS 域名自解析)

ios 之 netty版本swiftNio(DNS 域名自解析)

时间:2024-04-07 13:32:47浏览次数:19  
标签:netty addr self ios host let swiftNio DNS port

SwiftNio 简介


用于高性能协议服务器和客户端的事件驱动、无阻塞的网络应用程序框架。

SwiftNIO是一个跨平台异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
这就像Netty,但是为Swift写的。

Xcode引入swiftNio


        在实际写代码前,我们需要把 SwiftNIO 加入我们的项目。我这里都是创建的 Swift Package 项目并通过 SwiftPM 添加依赖的。在 Xcode 的创建项目页面中,选择 Multiplatform 中的 Swift Package。在swiftNio中选择自己需要的库,他的库分别为

DNS简介

        DNS是一种可以将域名和IP地址相互映射的以层次结构分布的数据库系统。DNS系统采用递归查询请求的方式来响应用户的查询,为互联网的运行提供关键性的基础服务。目前绝大多数的防火墙和网络都会开放DNS服务,DNS数据包不会被拦截,因此可以基于DNS协议建立隐蔽信道,从而顺利穿过防火墙,在客户端和服务器之间传输数据。 [1]

        DNS允许终端用户设备将给定的人类可读URL转换为网络可以理解的机器可用IP地址。Internet Engineering Task Force(IETF)标准组对HTTP和DNS进行了标准化定义。原始的DNS标准是在1987年发布的,因为用户在使用网络浏览器的同时需要使用其他应用程序,例如将电子邮件地址转换为IP地址。

SwiftNio DNS解析创建

        新建一个类CustomAddrinfoResolver 实现 Resolver,重新 initiateAAAAQuery 方法来实现域名解析到指定的IP地址。



import Foundation
import NIOPosix
import NIOCore
import Dispatch

extension NIOBSDSocket {
    /// Specifies the type of socket.
    public struct SocketType: RawRepresentable {
        public typealias RawValue = CInt
        public var rawValue: RawValue
        public init(rawValue: RawValue) {
            self.rawValue = rawValue
        }
    }
}

extension NIOBSDSocket.SocketType: Equatable {
}

extension NIOBSDSocket.SocketType: Hashable {
}

// A thread-specific variable where we store the offload queue if we're on an `SelectableEventLoop`.
let offloadQueueTSV = ThreadSpecificVariable<DispatchQueue>()

public class CustomAddrinfoResolver:Resolver{
    
    private let v4Future: EventLoopPromise<[SocketAddress]>
    private let v6Future: EventLoopPromise<[SocketAddress]>
    private let host:String
    private let ipAddress:String
//    private let aiSocktype: NIOBSDSocket.SocketType
    private let aiProtocol: NIOBSDSocket.OptionLevel
    
    /// Create a new resolver.
    ///
    /// - parameters:
    ///     - loop: The `EventLoop` whose thread this resolver will block.
    ///     - aiSocktype: The sock type to use as hint when calling getaddrinfo.
    ///     - aiProtocol: the protocol to use as hint when calling getaddrinfo.
    init(loop: EventLoop,aiProtocol: NIOBSDSocket.OptionLevel,host:String,ipAddress:String) {
        self.v4Future = loop.makePromise()
        self.v6Future = loop.makePromise()
//    aiSocktype: NIOBSDSocket.SocketType,
//        self.aiSocktype = aiSocktype
        self.aiProtocol = aiProtocol
        self.host = host
        self.ipAddress = ipAddress
    }
    
    /// Initiate a DNS A query for a given host.
    ///
    /// Due to the nature of `getaddrinfo`, we only actually call the function once, in the AAAA query.
    /// That means this just returns the future for the A results, which in practice will always have been
    /// satisfied by the time this function is called.
    ///
    /// - parameters:
    ///     - host: The hostname to do an A lookup on.
    ///     - port: The port we'll be connecting to.
    /// - returns: An `EventLoopFuture` that fires with the result of the lookup.
    public func initiateAQuery(host: String, port: Int) -> EventLoopFuture<[SocketAddress]> {
        self.offloadQueue().async {
            self.resolveBlocking(host: host, port: port)
        }
        return v4Future.futureResult
    }

    /// Initiate a DNS AAAA query for a given host.
    ///
    /// Due to the nature of `getaddrinfo`, we only actually call the function once, in this function.
    /// That means this function call actually blocks: sorry!
    ///
    /// - parameters:
    ///     - host: The hostname to do an AAAA lookup on.
    ///     - port: The port we'll be connecting to.
    /// - returns: An `EventLoopFuture` that fires with the result of the lookup.
    public func initiateAAAAQuery(host: String, port: Int) -> EventLoopFuture<[SocketAddress]> {
        self.offloadQueue().async {
            self.resolveBlocking(host: host, port: port)
        }
        return v6Future.futureResult
    }

    private func offloadQueue() -> DispatchQueue {
        if let offloadQueue = offloadQueueTSV.currentValue {
            return offloadQueue
        } else {
            if MultiThreadedEventLoopGroup.currentEventLoop != nil {
                // Okay, we're on an SelectableEL thread. Let's stuff our queue into the thread local.
                let offloadQueue = DispatchQueue(label: "io.swiftnio.GetaddrinfoResolver.offloadQueue")
                offloadQueueTSV.currentValue = offloadQueue
                return offloadQueue
            } else {
                return DispatchQueue.global()
            }
        }
    }

    /// Cancel all outstanding DNS queries.
    ///
    /// This method is called whenever queries that have not completed no longer have their
    /// results needed. The resolver should, if possible, abort any outstanding queries and
    /// clean up their state.
    ///
    /// In the getaddrinfo case this is a no-op, as the resolver blocks.
    public func cancelQueries() { }

    /// Perform the DNS queries and record the result.
    ///
    /// - parameters:
    ///     - host: The hostname to do the DNS queries on.
    ///     - port: The port we'll be connecting to.
    ///     //IP to Int
    ///
    func IpToInt(ip : String)-> Int{
        
        var array = ip.components(separatedBy: ".")
        while array.count < 4 {
            array.append("0")
        }
        let ip1 =  Int(array[3]) ?? 0
        let ip2 =  Int(array[2]) ?? 0
        let ip3 =  Int(array[1]) ?? 0
        let ip4 =  Int(array[0]) ?? 0
        let address = ip1<<24 | ip2<<16 | ip3<<8 | ip4<<0;
        return address
    }
    func portToBytes(port : Int)-> [Int8]{
        
        return [
                Int8(truncatingIfNeeded: port >> 8),
                Int8(truncatingIfNeeded: port)
           ]
    }
    private func resolveBlocking(host: String, port: Int) {
        let arr = self.ipAddress.components(separatedBy: ".")
        var isRight = true
        for str in arr{
            let num = Int(str) ?? -1
            if(num >= 255 || num < 0){
                isRight = false
            }
        }

        if(self.ipAddress.count > 0 && isRight){
            
            let bytes = self.portToBytes(port: port)
            let newytes = [bytes[1],bytes[0]]
            let data = Data.init(bytes:newytes, count:newytes.count)
            let newPort = data.lyz_2BytesToInt()
            
            var v4Results: [SocketAddress] = []
            let ip_in_addr_t : Int64 =  Int64(self.IpToInt(ip: self.ipAddress))
            let sin_addr = in_addr.init(s_addr:in_addr_t(ip_in_addr_t))
            let sin_port = in_port_t(newPort)
            let socket_addr = sockaddr_in.init(sin_len:16, sin_family:2, sin_port:sin_port, sin_addr:sin_addr, sin_zero:(0,0,0,0,0,0,0,0))
            let socketAddressV4 = SocketAddress.init(socket_addr, host: host)
            v4Results.append(socketAddressV4)
            v4Future.succeed(v4Results)
            return
        }else{
            var info: UnsafeMutablePointer<addrinfo>?
            var hint = addrinfo()
            hint.ai_protocol = self.aiProtocol.rawValue
            guard getaddrinfo(host, String(port), &hint, &info) == 0 else {
                self.fail(SocketAddressError.unknown(host: host, port: port))
                return
            }
    //        0000101110100101
            if let info = info {
                self.parseAndPublishResults(info, host: host)
                freeaddrinfo(info)
            } else {
                /* this is odd, getaddrinfo returned NULL */
                self.fail(SocketAddressError.unsupported)
            }
        }
    }

    /// Parses the DNS results from the `addrinfo` linked list.
    ///
    /// - parameters:
    ///     - info: The pointer to the first of the `addrinfo` structures in the list.
    ///     - host: The hostname we resolved.

    internal typealias CAddrInfo = addrinfo

    private func parseAndPublishResults(_ info: UnsafeMutablePointer<CAddrInfo>, host: String) {
        var v4Results: [SocketAddress] = []
        var v6Results: [SocketAddress] = []

        var info: UnsafeMutablePointer<CAddrInfo> = info
        while true {
            let addressBytes = UnsafeRawPointer(info.pointee.ai_addr)
            switch NIOBSDSocket.AddressFamily(rawValue: info.pointee.ai_family) {
            case .inet:
                // Force-unwrap must be safe, or libc did the wrong thing.
                var addr = addressBytes!.load(as: sockaddr_in.self)
                let socketAddressV4 = SocketAddress.init(addr, host: host)
//                addr.sin_addr =
                v4Results.append(socketAddressV4)
//                v4Results.append(.init(, host: <#T##String#>))
            case .inet6:
                // Force-unwrap must be safe, or libc did the wrong thing.
                v6Results.append(.init(addressBytes!.load(as: sockaddr_in6.self), host: host))
            default:
                self.fail(SocketAddressError.unsupported)
                return
            }

            guard let nextInfo = info.pointee.ai_next else {
                break
            }

            info = nextInfo
        }
        v6Future.succeed(v6Results)
        v4Future.succeed(v4Results)
    }

    /// Record an error and fail the lookup process.
    ///
    /// - parameters:
    ///     - error: The error encountered during lookup.
    private func fail(_ error: Error) {
        self.v6Future.fail(error)
        self.v4Future.fail(error)
    }
    
    
}

CustomAddrinfoResolver使用

   在上一篇章的ios 之 netty版本swiftNio(socket创建)基础上绑定CustomAddrinfoResolver,即可实现域名解析到指定的IP地址。

  /**
     创建socket 连接
     */
    public func createSocket(address:String,port:Int,ipAddress:String? = "",envNum : String){
//    sslContext:NIOSSLContext
        self.host = address;
        self.IP = ipAddress ?? "";
        self.port = port;
        self.envNum = envNum;
        if(address.count == 0){
            return
        }
        self.startNioTimer(isConnected: true)
        group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
        readHanddle = EchoClientHandler()
        readHanddle?.didReadBytes = {[weak self](bytes) in
            self?.lastReadTime = Common.getTimestampWithAccuracyInMilliseconds()
            self?.didReadBytes?(bytes)
        }
        readHanddle?.statusChange = {[weak self](state, err) in
            if(state == self?.status){
                //防止重复
                return
            }
            if(state == .none){
                self?.stopTimer()
                self?.status = .none
            }else{
                self?.status = .connected
                self?.lastReadTime = DateTool.getCurrentTimeStamp()
                DispatchQueue.main.async {
                    self?.startNioTimer(isConnected:false)
                }
            }
            DispatchQueue.main.async {
                self?.statusChange?(state == .connected,err)
            }
            
        }
        
        let bootstrap = ClientBootstrap(group: group!)
            .resolver(getResolver(group: group!,host:address,ipAddress: ipAddress!))
            // Enable SO_REUSEADDR.
            .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
            .channelInitializer {[weak self] channel in
                //add context ssl
                guard let strongself = self else{
                    DispatchQueue.main.async {
                        LogTool.addlog("创建Nio Socket 失败", type: 2,success: 0)
                        self?.statusChange?(false,nil)
                    }
                    return channel.pipeline.addHandler(EchoClientHandler())
                }
                if(envNum.contains("gm")){
                    let sslHandler = self!.createGmSSL(address: address)
                    return  channel.pipeline.addHandler(sslHandler).flatMap({
                        channel.pipeline.addHandler(self!.readHanddle ?? EchoClientHandler())
                    })// Fallback on earlier versions
                }else{
                    let sslHandler = self!.createSSL(address: address, env:self!.envNum)
                    return  channel.pipeline.addHandler(sslHandler).flatMap({
                        channel.pipeline.addHandler(self!.readHanddle!)
                    }) // Fallback on earlier versions
                }
            }
          self.status = .connecting
          eventLoopfuture =  bootstrap.connect(host: address, port: port)
    
    }
    
    public func getResolver(group: EventLoopGroup,host:String,ipAddress:String)->Resolver{
        let resolver = CustomAddrinfoResolver(loop: group.next(), aiProtocol: .tcp,host: host, ipAddress:ipAddress)
//       let v4Future = resolver.initiateAQuery(host: "127.0.0.1", port: 12345)
//       let v6Future = resolver.initiateAAAAQuery(host: "127.0.0.1", port: 12345)
       return resolver
    }

标签:netty,addr,self,ios,host,let,swiftNio,DNS,port
From: https://blog.csdn.net/karision/article/details/137460524

相关文章

  • ios 之 netty版本swiftNio(TLSHandler 创建)
    SwiftNio简介用于高性能协议服务器和客户端的事件驱动、无阻塞的网络应用程序框架。SwiftNIO是一个跨平台异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。这就像Netty,但是为Swift写的。Xcode引入swiftNio        在实际写代码前,......
  • 关于Axios的异域问题
    需要建一个类,在类中修改需要访问的前端端口: 代码如下:importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.UrlB......
  • 面试常问问题——ios测试和安卓测试的区别
    1.Android长按home键呼出应用列表和切换应用,然后右滑则终止应用; 2.多分辨率测试,Android端20多种,ios较少; 3.手机操作系统,Android较多,ios较少且不能降级,只能单向升级;新的ios系统中的资源库不能完全兼容低版本中的ios系统中的应用,低版本ios系统中的应用调用了新的资源库,会直接......
  • 折腾PXE网络启动 pxe 双引导bios&uefi模式 WDS windows deployment server
    简介:这才是最终章。折腾这么多,其实还是为了WDS。折腾TFTPD引导bios,是为了确认引导文件可以引导maxdos。折腾TFTPD引导uefi,也是为了确认可以引导grub。折腾OPENWRT双引导bios和UEFI,是为了确认DHCPoption93。现在我们有了可以双引导的TFTP-ROOT目录,虽然只有4个文件,这足够我......
  • 折腾PXE网络启动 pxe 双引导bios&uefi模式 OPENWRT
    简介:前两篇已经折腾了pxe引导bios和uefi,甭管启动的是啥,已经可以网络引导了。但是同时面对这两种系统的时候怎么办?需要通过dhcp的参数来控制谁启动什么。核心内容RFC4578:DynamicHostConfigurationProtocol(DHCP)OptionsfortheIntelPrebooteXecutionEnvironment(......
  • axios快速入门
    一、环境配置1.1概述上古浏览器页面在向服务器请求数据时,因为返回的是整个页面的数据,页面都会强制刷新一下,这对于用户来讲并不是很友好。并且我们只是需要修改页面的部分数据,但是从服务器端发送的却是整个页面的数据,十分消耗网络资源。而我们只是需要修改页面的部分数据,也......
  • axios的理解和使用
    axios中文文档 https://www.axios-http.cn/docs/intro是什么前端最流行的ajax请求库react/vue官方都推荐使用axios发ajax请求 特点基本promise的异步ajax请求库浏览器端/node端都可以使用支持请求/响应拦截器支持请求取消请求/响应数据转换批量发送多个请求常用语法......
  • 苹果电脑(Mac os系统)和iPhone手机(ios系统)实现发送iMessages短信的全部方式
    一、iphone手机(IOS系统)上群发总结为以下几种方式/*经测试,使用iphone手机进行iMessage群发分越狱和免越狱版,越狱版可以做到通过修改序列号来达到无限次数更换ID,免越狱iphone只可以做到大约60、70次更换ID此后将无法再更换ID。*/1、通过iphone手机上自带的快捷指令,来代替人工......
  • axios 常见状态码
    '100':'Continue','101':'SwitchingProtocols','102':'Processing','103':'EarlyHints','200':'Ok','201':'Created&......
  • vue axios sessionID 每次请求都不同的解决方式
    前端:        后端:注意:配置 allowedOrigins时,如果写的是http://localhost/,而请求的源地址是127.0.0.1。虽然它们通常指向同一台本地计算机,但在CORS规则中被视为不同的源。需更新更新allowedOrigins列表,将现有条目http://localhost:5174替换为http://127.......