Эффективная передача больших байтов с использованием MultipeerConnectivity в SwiftIOS

Программируем под IOS
Ответить
Anonymous
 Эффективная передача больших байтов с использованием MultipeerConnectivity в Swift

Сообщение Anonymous »

Описание
Моя цель — генерировать большие данные на одном устройстве, передавать их на другое с помощью потоков и сохранять на диск. Чтобы оптимизировать использование памяти, я планирую последовательно генерировать и отправлять меньшие фрагменты.
Предположим, размер передаваемого файла составляет до 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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»