【问题标题】:Convert NSData to sockaddr struct in swift [duplicate]快速将 NSData 转换为 sockaddr 结构 [重复]
【发布时间】:2014-07-17 04:39:07
【问题描述】:

我正在尝试快速进行简单的 DNS 查找。到目前为止,这是我拥有的代码:

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

此时,"addresses" NSArray 中的每个元素都是一个包装了 sockaddr 结构的 CFDataRef 对象。

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

for address: AnyObject in addresses {
  println(address)  // address is of type NSData.
}

到目前为止一切顺利(我认为)。当我在单元测试中运行它时,这会打印出看起来有效的数据。这是我卡住的地方。对于我的生活,我无法弄清楚如何将 NSData 对象中的字节转换为 sockaddr 结构。

如何将 COpaquePointer? 类型的 address.bytes 转换为 c 结构?任何帮助表示赞赏。我正在努力解决这个问题。

【问题讨论】:

    标签: ios swift core-foundation


    【解决方案1】:

    对于使用getnameinfo 的更简单的解决方案,请在此处查看 Martin 的回答:How can I get a real IP address from DNS query in Swift?

    为 Swift 5 / IPv6 更新:

    CFHostGetAddressing 返回的对象可以作为Data 桥接到 Swift,并使用withUnsafeBytesassumingMemoryBound(to:) 转换为in_addr/in6_addr

    这是一个使用inet_ntop 将 IPv4/IPv6 地址转换为字符串的完整示例:

    import CFNetwork
    import Foundation
    
    protocol NetworkAddress {
        static var family: Int32 { get }
        static var maxStringLength: Int32 { get }
    }
    extension in_addr: NetworkAddress {
        static let family = AF_INET
        static let maxStringLength = INET_ADDRSTRLEN
    }
    extension in6_addr: NetworkAddress {
        static let family = AF_INET6
        static let maxStringLength = INET6_ADDRSTRLEN
    }
    
    extension String {
        init<A: NetworkAddress>(address: A) {
            // allocate a temporary buffer large enough to hold the string
            var buf = ContiguousArray<Int8>(repeating: 0, count: Int(A.maxStringLength))
            self = withUnsafePointer(to: address) { rawAddr in
                buf.withUnsafeMutableBufferPointer {
                    String(cString: inet_ntop(A.family, rawAddr, $0.baseAddress, UInt32($0.count)))
                }
            }
        }
    }
    
    func addressToString(data: Data) -> String? {
        return data.withUnsafeBytes {
            let family = $0.baseAddress!.assumingMemoryBound(to: sockaddr_storage.self).pointee.ss_family
            // family determines which address type to cast to (IPv4 vs IPv6)
            if family == numericCast(AF_INET) {
                return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in.self).pointee.sin_addr)
            } else if family == numericCast(AF_INET6) {
                return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in6.self).pointee.sin6_addr)
            }
            return nil
        }
    }
    
    let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
    var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
    let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [Data]?
    
    print(addresses?.compactMap(addressToString))
    

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

    var data: NSData ...
    var address: sockaddr ...
    
    data.getBytes(&address, length: MemoryLayout<sockaddr>.size)
    

    为 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))
        }
    }
    


    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))
        }
    }
    


    不幸的是,这需要首先初始化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))
    }
    

    或者这个:

    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))
    }
    

    更多讨论请见Swift: Pass Uninitialized C Structure to Imported C function

    【讨论】:

    • 这甚至可以简化为:data.getBytes(&addr, length:sizeof(sockaddr))
    • 好点。我更新了我的答案,还添加了有关在不先初始化结构的情况下做同样事情的信息。
    • 对于它的价值,您可能不想使用 sockaddr - 但您得到的实际 sockaddr 结构。像 sockaddr_in 或 sockaddr_un。否则你可能会遇到缓冲区溢出。也许你觉得这个扩展很有用:github.com/AlwaysRightInstitute/SwiftSockets/blob/master/…
    • 看来ptr.withUnsafePointer(body) 不再用Xcode 6 beta 4 编译了。
    • 我得到:'&' 与 'sockaddr_storage' 类型的非 inout 参数一起使用
    猜你喜欢
    • 1970-01-01
    • 2014-10-14
    • 2015-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-18
    相关资源
    最近更新 更多