Моя цель — генерировать большие данные на одном устройстве, передавать их на другое с помощью потоков и сохранять на диск. Чтобы оптимизировать использование памяти, я планирую последовательно генерировать и отправлять меньшие фрагменты.
Предположим, размер передаваемого файла составляет до 1,5 ГБ.
Текущая реализация
Моя основная логика MultipeerConnectivity инкапсулирована в классе MultipeerService. Ключевые части следующие:
Код: Выделить всё
class MultipeerService: NSObject, ObservableObject, MCSessionDelegate, MCNearbyServiceBrowserDelegate, MCNearbyServiceAdvertiserDelegate {
var inputStreamDelegate: DataReceiver?
...
func startStreaming(withName streamName: String, to peer: MCPeerID) throws -> OutputStream {
return try session.startStream(withName: streamName, toPeer: peer)
}
public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
log.info("Started receiving stream '\(streamName)'")
self.inputStreamDelegate?.readStream(stream, withName: streamName)
}
}
Класс DataSender обрабатывает генерацию байтов и запись их в поток:
Код: Выделить всё
class DataSender: NSObject, StreamDelegate, ObservableObject {
private var multipeerService: MultipeerService
private var outputStream: OutputStream?
private let chunkSize = 1024 // 1KB chunk size
private var totalSize: Int = 0
@Published var bytesSent: Int = 0
init(ms: MultipeerService = MultipeerService.shared) {
self.multipeerService = ms
super.init()
}
// Called from UI
func startStreaming(peer: MCPeerID, sizeMB: Int) {
do {
let outputStream = try multipeerService.startStreaming(withName: "Test", to: peer)
self.outputStream = outputStream
outputStream.delegate = self
outputStream.schedule(in: .main, forMode: .common)
outputStream.open()
self.totalSize = sizeMB * 1024 * 1024 // convert MB to bytes
self.bytesSent = 0
} catch {
fatalError("Error starting stream: \(error)")
}
}
private func sendRandomData() {
guard let outputStream = outputStream else { return }
while outputStream.hasSpaceAvailable && bytesSent < totalSize {
let currentChunkSize = min(chunkSize, totalSize - bytesSent)
var buffer = [UInt8](repeating: 0, count: currentChunkSize)
_ = SecRandomCopyBytes(kSecRandomDefault, currentChunkSize, &buffer)
let bytesWritten = outputStream.write(buffer, maxLength: currentChunkSize)
if bytesWritten > 0 {
DispatchQueue.main.async {
self.bytesSent += bytesWritten
}
print("Bytes written: \(bytesWritten) | Total: \(bytesSent)")
} else {
if let error = outputStream.streamError {
print("Stream Error: \(error)")
closeStream()
return
}
}
}
if bytesSent >= totalSize {
closeStream()
}
}
private func closeStream() {
print("Closing stream")
outputStream?.close()
outputStream?.remove(from: .main, forMode: .default)
outputStream = nil
}
// StreamDelegate methods
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
print("Stream called: \(eventCode)")
switch eventCode {
case .hasSpaceAvailable:
print("hasSpaceAvailable")
sendRandomData()
case .endEncountered:
print("Stream end encountered")
closeStream()
case .errorOccurred:
if let error = aStream.streamError {
print("Stream Error: \(error)")
}
closeStream()
default:
break
}
}
}
Класс DataReceiver считывает поток и записывает его в файл:
Код: Выделить всё
class DataReceiver: NSObject, StreamDelegate, ObservableObject {
private var multipeerService: MultipeerService
private var inputStream: InputStream?
private var fileHandle: FileHandle?
@Published var received: Int = 0
init(ms: MultipeerService = MultipeerService.shared) {
self.multipeerService = ms
super.init()
self.multipeerService.inputStreamDelegate = self
}
// Called by MultipeerService when stream is received
public func readStream(_ stream: InputStream, withName streamName: String) {
stream.delegate = self
stream.schedule(in: .main, forMode: .default)
stream.open()
self.inputStream = stream
// Prepare to write to file
if let fileURL = createFileInLibrary(withName: streamName) {
do {
fileHandle = try FileHandle(forWritingTo: fileURL)
} catch {
print("Error opening file handle: \(error)")
}
}
}
// StreamDelegate method to handle input stream events
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
print("Stream function called. EventCode: \(eventCode)")
switch eventCode {
case .hasBytesAvailable:
if aStream == inputStream {
readAvailableBytes(stream: aStream as! InputStream)
}
case .endEncountered:
closeStream()
case .errorOccurred:
if let error = aStream.streamError {
print("Stream Error: \(error)")
}
closeStream()
default:
break
}
}
private func readAvailableBytes(stream: InputStream) {
let bufferSize = 1024
var buffer = [UInt8](repeating: 0, count: bufferSize)
while stream.hasBytesAvailable {
let numberOfBytesRead = stream.read(&buffer, maxLength: bufferSize)
print("Number of bytes read: \(numberOfBytesRead)")
DispatchQueue.main.async {
self.received += numberOfBytesRead
}
if numberOfBytesRead < 0 {
if let error = stream.streamError {
print("Stream read error: \(error)")
return
}
}
// Write to file
if let fileHandle = fileHandle {
fileHandle.write(Data(bytes: buffer, count: numberOfBytesRead))
}
}
}
private func closeStream() {
inputStream?.close()
inputStream?.remove(from: .current, forMode: .default)
inputStream = nil
fileHandle?.closeFile()
fileHandle = nil
}
private func createFileInLibrary(withName name: String) -> URL? {
let fileManager = FileManager.default
guard let libraryDirectory = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first else {
print("Could not find library directory")
return nil
}
let fileURL = libraryDirectory.appendingPathComponent("\(name).data")
fileManager.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
return fileURL
}
}
Первый узел отправляет около 17 000 байт, а затем останавливается, а второй узел считывает только около 2 000 байт. и останавливается. Кроме того, если я использую .current вместо .main для потока.schedule, StreamDelegate никогда не вызывается.
Вывод консоли от узла-получателя
Код: Выделить всё
Stream function called. EventCode: NSStreamEvent(rawValue: 1)
Stream function called. EventCode: NSStreamEvent(rawValue: 2)
Number of bytes read: 1024
Number of bytes read: 1024
Number of bytes read: 142
Как добиться желаемого результата и эффективной потоковой передачи больших данных между устройствами с помощью MultipeerConnectivity? Буду очень признателен за любые советы или решения проблем, с которыми я столкнулся.
Подробнее здесь: https://stackoverflow.com/questions/787 ... y-in-swift
Мобильная версия