在swift中将NSData转换为sockaddr结构

kgr*_*nek 10 core-foundation ios swift

我正在尝试在swift中进行简单的DNS查找.到目前为止,这是我的代码:

let hostRef = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil)
let addresses = CFHostGetAddressing(hostRef, &resolved).takeRetainedValue() as NSArray
Run Code Online (Sandbox Code Playgroud)

此时,"地址"NSArray中的每个元素都是一个包装sockaddr结构的CFDataRef对象.

由于CFDataRef可以免费桥接到NSData,我可以像这样循环遍历它们:

for address: AnyObject in addresses {
  println(address)  // address is of type NSData.
}
Run Code Online (Sandbox Code Playgroud)

到目前为止这么好(我想).当我在单元测试中运行时,这会打印出有效的数据.这是我陷入困境的地方.对于我的生活,我无法弄清楚如何将NSData对象中的字节转换为sockaddr结构.

如何将address.bytes(类型为COpaquePointer?)转换为ac结构?任何帮助赞赏.我正试图弄清楚我的头撞墙.

jtb*_*des 10

您可以使用NSData方法getBytes(_, length:)方法并使用前缀&运算符将sockaddr结构传递给inout参数:

var data: NSData ...
var address: sockaddr ...

data.getBytes(&address, length: MemoryLayout<sockaddr>.size)
Run Code Online (Sandbox Code Playgroud)

针对Swift 3进行了更新:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
    var storage = sockaddr_storage()
    data.getBytes(&storage, length: MemoryLayout<sockaddr_storage>.size)

    if Int32(storage.ss_family) == AF_INET {
        let addr4 = withUnsafePointer(to: &storage) {
            $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
                $0.pointee
            }
        }

        // prints 74.125.239.132
        print(String(cString: inet_ntoa(addr4.sin_addr), encoding: .ascii))
    }
}
Run Code Online (Sandbox Code Playgroud)

2015年6月3日更新: 现在C结构可以很容易地进行零初始化,这变得更加简单:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(host, .Addresses, nil)
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
    var storage = sockaddr_storage()
    data.getBytes(&storage, length: sizeof(sockaddr_storage))

    if Int32(storage.ss_family) == AF_INET {
        let addr4 = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory }

        // prints 74.125.239.132
        println(String(CString: inet_ntoa(addr4.sin_addr), encoding: NSASCIIStringEncoding))
    }
}
Run Code Online (Sandbox Code Playgroud)


不幸的是,这需要sockaddr首先进行初始化.为避免这种情况,您可以执行以下操作:

func makeWithUnsafePointer<T>(body: UnsafePointer<T> -> ()) -> T {
    let ptr = UnsafePointer<T>.alloc(sizeof(T))
    body(ptr)
    return ptr.move()
}

let addr: sockaddr = makeWithUnsafePointer {
    data.getBytes($0 as UnsafePointer<sockaddr>, length: sizeof(sockaddr))
}
Run Code Online (Sandbox Code Playgroud)

或这个:

func makeWithUninitialized<T>(body: inout T -> ()) -> T {
    let ptr = UnsafePointer<T>.alloc(sizeof(T))
    body(&ptr.memory)
    return ptr.move()
}

let addr = makeWithUninitialized { (inout addr: sockaddr) in
    data.getBytes(&addr, length: sizeof(sockaddr))
}
Run Code Online (Sandbox Code Playgroud)

有关更多讨论,请参阅Swift:将未初始化的C结构传递给导入的C函数

  • 我得到:'&'与'sockaddr_storage'类型的非inout参数一起使用 (3认同)
  • 对于它的价值,你可能不想使用sockaddr - 但你得到的实际的sockaddr结构.像sockaddr_in或sockaddr_un一样.否则你可能会遇到缓冲区溢出.也许你觉得这个扩展很有用:https://github.com/AlwaysRightInstitute/SwiftSockets/blob/master/ARISockets/SocketAddress.swift (2认同)