2014-07-17 67 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