我需要与具有特殊消息格式的服务器通信:每个消息以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()方法调用得太早了,虽然我使用了屏障!我做错了什么?
答案 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操作的类型。
或者,您可以访问不同类型的相同内存,而无需通过无类型内存访问进行重新绑定,只要绑定类型和目标类型是普通类型即可。