从InputStream

时间:2018-01-17 20:01:32

标签: ios swift sockets inputstream endianness

我需要与具有特殊消息格式的服务器通信:每个消息以4个字节开头(一起是大端格式的无符号long / UInt32),它确定以下消息的长度。在这4个字节之后,消息将作为普通字符串发送

所以我首先需要将4个字节读入整数(32位无符号)。在Java中,我这样做:

DataInputStream is;
...
int len = is.readInt();

如何在Swift 4中执行此操作?

目前我使用

var lengthbuffer = [UInt8](repeating: 0, count: 4)
let bytecount = istr.read(&lengthbuffer, maxLength: 4)
let lengthbytes = lengthbuffer[0...3]
let bigEndianValue = lengthbytes.withUnsafeBufferPointer {
            ($0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 1) { $0 })
            }.pointee
let bytes_expected = Int(UInt32(bigEndian: bigEndianValue))

但这看起来并不像这是最优雅的方式。而且,有时(我无法可靠地再现它)有一个错误的值读取(太大)。当我尝试为以下消息分配内存时,应用程序崩溃:

let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes_expected)
let bytes_read = istr.read(buffer, maxLength: bytes_expected)

那么从InputStream读取UInt32的快捷方式是什么?

编辑:

我当前的代码(从评论中实现的内容。谢谢!)看起来像这样:

private let inputStreamAccessQueue  = DispatchQueue(label: "SynchronizedInputStreamAccess")  // NOT concurrent!!!

// This is called on Stream.Event.hasBytesAvailable
func handleInput() {
    self.inputStreamAccessQueue.sync(flags: .barrier) {            
        guard let istr = self.inputStream, istr.hasBytesAvailable else {
            log.error(self.buildLogMessage("handleInput() called when inputstream has no bytes available"))
            return
        }

        let lengthbuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 4)
        defer { lengthbuffer.deallocate(capacity: 4) }
        let lenbytes_read = istr.read(lengthbuffer, maxLength: 4)

        guard lenbytes_read == 4 else {
            self.errorHandler(NetworkingError.InputError("Input Stream received \(lenbytes_read) (!=4) bytes"))
            return
        }

        let bytes_expected = Int(UnsafeRawPointer(lengthbuffer).load(as: UInt32.self).bigEndian)
        log.info(self.buildLogMessage("expect \(bytes_expected) bytes"))

        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes_expected)
        let bytes_read = istr.read(buffer, maxLength: bytes_expected)
        guard bytes_read == bytes_expected else {
            print("Error: Expected \(bytes_expected) bytes, read \(bytes_read)")
            return
        }

        guard let message = String(bytesNoCopy: buffer, length: bytes_expected, encoding: .utf8, freeWhenDone: true) else {
            log.error("ERROR WHEN READING")
            return
        }

        self.handleMessage(message)
    }
}

这大部分时间都有效,但有时istr.read()不读取bytes_expected字节,而是读取bytes_read&lt; bytes_expected。这导致另一个hasbytesAvailable事件和handleInput()再次被调用。当然,这一次,读取的前4个字节不包含新消息的长度,而是包含最后一条消息的一些内容。但我的代码不知道,所以第一个字节被解释为长度。在许多情况下,这是一个真正的大值=&gt;分配太多内存=&gt;碰撞

我认为这是对bug的解释。但是如何解决呢? 当hasBytesAvailable = true时,在流上调用read()?可能有更好的解决方案吗?

我认为当我循环时,hasBytesAvailableEvent仍会在每次read()=&gt;之后发生。 handleInput仍然会被太早调用...我怎么能避免这种情况?

编辑2:我现在已经实现了循环,不幸的是它仍然崩溃了同样的错误(可能是同样的原因)。相关代码:

let bytes_expected = Int(UnsafeRawPointer(lengthbuffer).load(as: UInt32.self).bigEndian)

var message = ""
var bytes_missing = bytes_expected
while bytes_missing > 0 {
    print("missing", bytes_missing)
    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes_missing)
    let bytes_read = istr.read(buffer, maxLength: bytes_missing)

    guard bytes_read > 0 else {
        print("bytes_read not <= 0: \(bytes_read)")
        return
    }

    guard bytes_read <= bytes_missing else {
        print("Read more bytes than expected. missing=\(bytes_missing), read=\(bytes_read)")
        return
    }

    guard let partial_message = String(bytesNoCopy: buffer, length: bytes_expected, encoding: .utf8, freeWhenDone: true) else {
        log.error("ERROR WHEN READING")
        return
    }

    message = message + partial_message
    bytes_missing -= bytes_read
}

崩溃时我的控制台输出:

  

缺少1952807028 malloc: * mach_vm_map(size = 1952808960)失败   (错误代码= 3)   * 错误:无法分配区域   ***在malloc_error_break中设置断点以进行调试

所以似乎整个handleInput()方法调用得太早了,虽然我使用了屏障!我做错了什么?

1 个答案:

答案 0 :(得分:2)

我会这样做(准备粘贴到游乐场):

import Foundation

var stream = InputStream(data: Data([0,1,0,0]))
stream.open()
defer { stream.close() }

var buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 4)
defer { buffer.deallocate(capacity: 4) }
guard stream.read(buffer, maxLength: 4) >= 4 else {
    // handle all cases: end of stream, error, waiting for more data to arrive...
    fatalError()
}
let number = UnsafeRawPointer(buffer).load(as: UInt32.self)
number // 256
number.littleEndian // 256
number.bigEndian // 65536

直接使用UnsafeRawPointer.load(没有显式重新绑定)对于简单类型according to the documentation是安全的。普通类型通常是那些不需要ARC操作的类型。

  

或者,您可以访问不同类型的相同内存,而无需通过无类型内存访问进行重新绑定,只要绑定类型和目标类型是普通类型即可。