从 NSMutableData 添加和提取对象

Appending and extracting objects from NSMutableData

我需要创建一个包含多个参数的数据流,通过网络发送它,然后在收到数据时提取这些参数。 这就是创建我的数据的方式(我确定我的所有变量都包含一个值)

 let dataToSend = NSMutableData()
 var mType = Int32(messageType.rawValue)
 var reqId = Int32(requestId)
 dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
 dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
 /* extra protocol data length. In version 0, this is0 as thereis no extra data.
 In the future, if you need to add extra protocol data use this*/
 var unUsedProtocol = Int32(0)
 dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
 var encodedDataJson = !jsonString.isEmptyOrNil ? jsonString?.asciiValues : [UInt8]()
 dataToSend.appendDataWithUnsafeBytes(from: &encodedDataJson, of: [UInt8]?.self)
 var bData = bindaryData
 dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)

这是我的 appendDataWithUnsafeBytes NSMutableData 扩展。

    extension NSMutableData {
        func appendDataWithUnsafeBytes<T>(from element: inout T, of type: T.Type) {
            let size = MemoryLayout.size(ofValue: element)
            withUnsafeBytes(of: &element) { ptr in
                let buffer = ptr.bindMemory(to: type)
                if let address = buffer.baseAddress {
                    self.append(address, length: size)
                } else {
                    VsLogger.logDebug("appendDataWithUnsafeBytes", "unable to get base address of pointer of type: \(type)")
                }
            }
        }
    }

这就是尝试提取它的方法(我得到了索引值和数据)

        var messageTypeValue: Int32? = nil
        var requestId: Int32? = nil
        var encodedJsonData: Data? = nil
        var binaryData: Data? = nil
        let intSize = MemoryLayout<Int32>.size
        let dataSize = MemoryLayout<Data>.size
        var offset = index
        
        bufferData.getBytes(&messageTypeValue, range: NSRange(location: offset, length: intSize))
        offset += intSize //8
        bufferData.getBytes(&requestId, range: NSRange(location: offset, length: intSize))
        offset += intSize //12
        /*skipping extra bytes (unsuedProtocol in sendMessageFunction). They come from a future version
         that this code doesn't understand*/
        offset += intSize //16
        bufferData.getBytes(&encodedJsonData, range: NSRange(location: offset, length: dataSize))
        offset += dataSize //32
        bufferData.getBytes(&binaryData, range: NSRange(location: offset, length: dataSize))

我只能得到第一个值 (messageTypeValue),但对于其余的值,我要么得到 nil,要么得到不正确的数据。

谢谢!

***** 更新 *****

我通过如下修改我的发送和接收函数来让它工作。我寄到哪里。

            let dataToSend = NSMutableData()
            var mType = Int32(messageType.rawValue)
            var reqId = Int32(requestId)
            dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
            dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
            /* estra protocol data length. In version 0, this is0 as thereis no extra data.
             In the future, if you need to add extra protocol data use this*/
            var unUsedProtocol = Int32(0)
            dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
            var jsonData = Data(!jsonString.isEmptyOrNil ? jsonString!.asciiValues : [UInt8]())
            dataToSend.appendDataWithUnsafeBytes(from: &jsonData, of: Data.self)
            var bData = bindaryData
            dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)

我在哪里收到它

        var offset = Int(index)
        let int32Size = MemoryLayout<Int32>.size
        let dataSize = MemoryLayout<Data>.size
        
        let messageTypeValue = (bufferData.bytes + offset).load(as: Int32.self)
        offset += int32Size
        let requestId = (bufferData.bytes + offset).load(as: Int32.self)
        offset += int32Size
        //skip this one since it not used
        //let unusedProtocol = (bufferData.bytes + offset).load(as: Int32.self)
        offset += int32Size
        let encodedJsonData = (bufferData.bytes + offset).load(as: Data.self)
        offset += dataSize
        let binaryData = bufferData.bytes.load(fromByteOffset: offset, as: Data.self)

数据应该始终正确对齐,但是有没有办法在 bufferData.bytes.load(fromByteOffset:as:)

上进行一些错误检查

如评论中所述,我强烈建议使用原生 Data

首先,您需要 将数字类型转换为 Data,反之亦然。我添加了自定义 append 方法

extension Data {

    init<T>(from value: T) {
        self = Swift.withUnsafeBytes(of: value) { Data([=10=]) }
    }

    func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
        var value: T = 0
        guard count >= MemoryLayout.size(ofValue: value) else { return nil }
        _ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: [=10=])} )
        return value
    }

    mutating func append<T>(_ other: T) {
        append(.init(from: other))
    }
} 

这是一个非常的数据集的简单版本,三个Int32值和一个JSON数组

let mType : Int32 = 1
let reqId : Int32 = 2
let unUsedProtocol : Int32 = 0
let json = try! JSONEncoder().encode(["hello", "world"])

为方便起见,我省略了错误处理。在生产代码中,不鼓励 try!

Data 的巨大好处是它可以被视为一个集合类型,一个 (UInt8) 字节的数组。要构建 Data 包,请转换数值并分别附加字节

var dataToSend = Data()
dataToSend.append(mType)
dataToSend.append(reqId)
dataToSend.append(unUsedProtocol)
dataToSend.append(json)

在接收端提取数值,这是一个辅助函数,它也会增加当前索引(如 inout 类型),字节长度由类型决定。函数 throws 如果索引超出范围且类型无法转换则出错。

enum ConvertDataError : Error { case outOfRange, invalidType}

func extractNumber<T : ExpressibleByIntegerLiteral>(from data : Data, type: T.Type, startIndex: inout Int) throws -> T {
    let endIndex = startIndex + MemoryLayout<T>.size
    guard endIndex <= data.endIndex else { throw ConvertDataError.outOfRange }
    let subdata = data[startIndex..<endIndex]
    guard let resultType = subdata.to(type: type) else { throw ConvertDataError.invalidType }
    startIndex = endIndex
    return resultType
}

获取数据包起始索引并提取Int32个值

var index = dataToSend.startIndex

do {
    let mType1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
    let reqId1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
    let unUsedProtocol1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
    print(mType1, reqId1, unUsedProtocol1)

对于 json 部分,您需要长度,在本例中为 17 个字节

    let jsonLength = json.count

    let jsonOffset = index
    index += jsonLength
    let json1 = try JSONDecoder().decode([String].self, from: dataToSend[jsonOffset..<index])
    print(json1)
} catch {
    print(error)
}