2014-07-17 33 views
10

我试图在swift中做一个简单的DNS查找。到目前为止,这里是我的代码:将NSData转换为swift中的sockaddr结构体

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

在这一点上,每一个元素中的“地址”的NSArray是一个CFDataRef对象包装一个套接字地址结构。

由于CFDataRef可拨打免费电话桥接NSData的,我可以通过他们循环,像这样:

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

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

如何将address.bytes(类型为COpaquePointer?)转换为c结构?任何帮助赞赏。试图弄清楚这一点,我正在撞墙。

回答

10

您可以使用NSData的方法getBytes(_, length:)方法和使用前缀&运营商的sockaddr结构传递到INOUT参数:

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

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

更新了斯威夫特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

+0

这甚至可以简化为:data.getBytes (&addr,长度:sizeof(sockaddr)) – kgreenek

+0

好点。我更新了我的答案,并且还添加了有关做同样的事情的信息,而无需首先初始化结构。 – jtbandes

+2

对于它的价值,你可能不想使用sockaddr - 但是你得到的实际sockaddr结构。像sockaddr_in或sockaddr_un。否则,你可能会遇到缓冲区溢出。也许你会发现这个扩展是有用的:https://github.com/AlwaysRightInstitute/SwiftSockets/blob/master/ARISockets/SocketAddress.swift – hnh