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