2017-06-04 91 views
2

我有這樣兩個結構:將結構寫入文件的最佳方法是什麼?

struct pcap_hdr_s { 
    UInt32 magic_number; 
    UInt16 version_major; 
    UInt16 version_minor; 
    int32_t thiszone; 
    UInt32 sigfigs; 
    UInt32 snaplen; 
    UInt32 network; 
}; 
//packet header 
struct pcaprec_hdr_s { 
    UInt32 ts_sec; 
    UInt32 ts_usec; 
    UInt32 incl_len; 
    UInt32 orig_len; 
}; 

被初始化(例如)如下:

let pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number: 0xa1b2c3d4, 
              version_major: 2, 
              version_minor: 4, 
              thiszone: 0, 
              sigfigs: 0, 
              snaplen: 
              pcap_record_size, 
              network: LINKTYPE_ETHERNET) 

    let pcapRecHeader : pcaprec_hdr_s = pcaprec_hdr_s(ts_sec: UInt32(ts.tv_sec), 
             ts_usec: UInt32(ts.tv_nsec), 
             incl_len: plen, 
             orig_len: length) 

我試圖創建的結構是這樣的數據/ NSData的對象:

  //write pcap header 
      let pcapHeaderData : NSData = NSData(bytes: pcapHeader, length: sizeofValue(pcapHeader)) 
      //write pcaprec header 
      let pcapRecHeaderData : NSData = NSData(bytes: pcapRecHeader, length: sizeofValue(pcapRecHeader)) 

但我總是得到這條錯誤的每一行:

"Connot convert value if type 'pcap_hdr_s' to expected arguemnt type 'UsafeRawPointer?'" 

我看了一下在Swift中的UnsafeRawPointers的文檔,但我沒有得到足夠的,現在,從結構創建NSData對象。 我在正確的路上,還是有更好的完成我的打算?

如果此數據初始化會的工作,我的下一個步驟將是

  • 追加pcapRecHeaderData到pcapHeaderData
  • 寫pcapHeaderData原子到文件/ URL與Data/NSData的的

所提供的功能編輯:

//packet ethernet header 
struct ethernet_hdr_s { 
    let dhost : [UInt8] 
    let shost : [UInt8] 
    let type : UInt16 
}; 

let src_mac : [UInt8] = [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB] 
let dest_mac : [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55] 

let ethernetHeader : ethernet_hdr_s = ethernet_hdr_s(dhost: dest_mac, shost: src_mac, type: 0x0800) 

編輯2:

let payloadSize = packet.payload.count 
      let plen = (payloadSize < Int(pcap_record_size) ? payloadSize : Int(pcap_record_size)); 

      bytesWritten = withUnsafePointer(to: &(packet.payload)) { 
       $0.withMemoryRebound(to: UInt8.self, capacity: Int(plen)) { 
        ostream.write($0, maxLength: Int(plen)) 
       } 
      } 
      if bytesWritten != (Int(plen)) { 
       // Could not write all bytes, report error ... 
       NSLog("error in Writting packet payload, not all Bytes written: bytesWritten: %d|plen: %d", bytesWritten, Int(plen)) 
      } 

回答

2

你可以寫任意數據到InputStream無需先創建一個 (NS)Data對象。「挑戰」是如何將指針轉換 的結構到UInt8指針由write方法預期:

let ostream = OutputStream(url: url, append: false)! // Add error checking here! 
ostream.open() 

var pcapHeader = pcap_hdr_s(...) 
let headerSize = MemoryLayout.size(ofValue: pcapHeader) 

let bytesWritten = withUnsafePointer(to: &pcapHeader) { 
     $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { 
     ostream.write($0, maxLength: headerSize) 
    } 
} 
if bytesWritten != headerSize { 
    // Could not write all bytes, report error ... 
} 

以同樣的方式,你可以從InputStream讀取數據:

let istream = InputStream(url: url)! // Add error checking here! 
istream.open() 

let bytesRead = withUnsafeMutablePointer(to: &pcapHeader) { 
    $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { 
     istream.read($0, maxLength: headerSize) 
    } 
} 
if bytesRead != headerSize { 
    // Could not read all bytes, report error ... 
} 

如果該文件可能在不同的平臺上創建了一個帶有 不同的字節順序,那麼你可以選中「魔術師」和交換字節 如果必要的話(如在https://wiki.wireshark.org/Development/LibpcapFileFormat描述):

switch pcapHeader.magic_number { 
case 0xa1b2c3d4: 
    break // Already in host byte order 
case 0xd4c3b2a1: 
    pcapHeader.version_major = pcapHeader.version_major.byteSwapped 
    pcapHeader.version_minor = pcapHeader.version_minor.byteSwapped 
    // ... 
default: 
    // Unknown magic, report error ... 
} 

爲了簡化寫入和讀出結構的任務的一個可限定 自定義擴展方法,例如

extension OutputStream { 

    enum ValueWriteError: Error { 
     case incompleteWrite 
     case unknownError 
    } 

    func write<T>(value: T) throws { 
     var value = value 
     let size = MemoryLayout.size(ofValue: value) 
     let bytesWritten = withUnsafePointer(to: &value) { 
      $0.withMemoryRebound(to: UInt8.self, capacity: size) { 
       write($0, maxLength: size) 
      } 
     } 
     if bytesWritten == -1 { 
      throw streamError ?? ValueWriteError.unknownError 
     } else if bytesWritten != size { 
      throw ValueWriteError.incompleteWrite 
     } 
    } 
} 

extension InputStream { 

    enum ValueReadError: Error { 
     case incompleteRead 
     case unknownError 
    } 

    func read<T>(value: inout T) throws { 
     let size = MemoryLayout.size(ofValue: value) 
     let bytesRead = withUnsafeMutablePointer(to: &value) { 
      $0.withMemoryRebound(to: UInt8.self, capacity: size) { 
       read($0, maxLength: size) 
      } 
     } 
     if bytesRead == -1 { 
      throw streamError ?? ValueReadError.unknownError 
     } else if bytesRead != size { 
      throw ValueReadError.incompleteRead 
     } 
    } 
} 

現在你可以讀寫簡單地

try ostream.write(value: pcapHeader) 
try istream.read(value: &pcapHeader) 

當然,這僅適用於「自足」結構喜歡你 pcap_hdr_spcaprec_hdr_s

+0

感謝這個很好的答案,但我被卡在了傳遞OutputStream文件url的地步。在將URL傳遞給OutputStream之前是否創建了文件,還是由流本身創建了該文件? – student96

+0

@ student96:我認爲現在已經澄清:) –

+0

是的,謝謝你的其他答案。我雖然它不是同一個問題,所以我創建了一個新的線程;) – student96

1

您可以轉換pcap_hdr_sData,反之亦然斯威夫特卡倫特3

  • pcap_hdr_s - >Data

    var pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number ... 
    
    let data = withUnsafePointer(to: &pcapHeader) { 
        Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: pcapHeader)) 
    } 
    
  • Data - >pcap_hdr_s

    let header: pcap_hdr_s = data.withUnsafeBytes { $0.pointee } 
    

參考:round trip Swift number types to/from Data

+0

如果(當,當)ABI發生變化時,保存這樣的數據將會中斷 – Alexander

+0

@Alexander什麼是ABI? – vadian

+0

ABI是「應用程序二進制接口」。 - @亞歷山大:pcap_hdr_s結構(在https://wiki.wireshark.org/Development/LibpcapFileFormat記錄)有一個「魔術」和版本號,它應該足以防止讀取不兼容的數據。 –

相關問題