Если вы хотите попробовать L2CAP, вы можете изменить приложение Central для настольных ПК следующим образом:
private let characteristicUUID = CBUUID(string: CBUUIDL2CAPPSMCharacteristicString)
...
Затем разместите рекламу и опубликуйте канал L2CAP:
let service = CBMutableService(type: peripheralUUID, primary: true)
let properties: CBCharacteristicProperties = [.read, .indicate]
let permissions: CBAttributePermissions = [.readable]
let characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: properties, value: nil, permissions: permissions)
self.characteristic = characteristic
service.characteristics = [characteristic]
self.manager.add(service)
self.manager.publishL2CAPChannel(withEncryption: false)
let data = [CBAdvertisementDataLocalNameKey : "Peripherial-42", CBAdvertisementDataServiceUUIDsKey: [peripheralUUID]] as [String : Any]
self.manager.startAdvertising(data)
В вашем
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
соответственно вашему
func peripheralManager(_ peripheral: CBPeripheralManager, didPublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
предлагает значение PSM (= вид дескриптора сокета (UInt16) для потоковых соединений Bluetooth):
let data = withUnsafeBytes(of: PSM) { Data($0) }
if let characteristic = self.characteristic {
characteristic.value = data
self.manager.updateValue(data, for: characteristic, onSubscribedCentrals: self.subscribedCentrals)
}
наконец в
func peripheralManager(_ peripheral: CBPeripheralManager, didOpen channel: CBL2CAPChannel?, error: Error?)
открыть поток ввода:
channel.inputStream.delegate = self
channel.inputStream.schedule(in: RunLoop.current, forMode: .default)
channel.inputStream.open()
где делегат может выглядеть примерно так:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasBytesAvailable:
if let stream = aStream as? InputStream {
...
//buffer is some UnsafeMutablePointer<UInt8>
let read = stream.read(buffer, maxLength: capacity)
print("\(read) bytes read")
}
case ...
}
iOS-приложение с центральной ролью
Предположим, у вас есть что-то подобное в вашем коде iOS:
func sendImage(imageData: Data) {
self.manager = CBCentralManager(delegate: self, queue: nil)
self.imageData = imageData
self.bytesToWrite = imageData.count
NSLog("start")
}
тогда вы можете изменить периферию на вашем iOS-клиенте для работы с каналом L2Cap следующим образом:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
if let characteristicValue = characteristic.value {
let psm = characteristicValue.withUnsafeBytes {
$0.load(as: UInt16.self)
}
print("using psm \(psm) for l2cap channel!")
peripheral.openL2CAPChannel(psm)
}
}
и как только вы получите уведомление об открытом канале, откройте на нем выходной поток:
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
...
channel.outputStream.delegate = self.streamDelegate
channel.outputStream.schedule(in: RunLoop.current, forMode: .default)
channel.outputStream.open()
Ваш предоставленный потоковый делегат может выглядеть так:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasSpaceAvailable:
if let stream = aStream as? OutputStream, let imageData = self.imageData {
if self.bytesToWrite > 0 {
let bytesWritten = imageData.withUnsafeBytes {
stream.write(
$0.advanced(by: totalBytes),
maxLength: self.bytesToWrite
)
}
self.bytesToWrite -= bytesWritten
self.totalBytes += bytesWritten
print("\(bytesWritten) bytes written, \(bytesToWrite) remain")
} else {
NSLog("finished")
}
}
case ...
Есть отличное видео WWDC 2017 года, Что нового в Core Bluetooth , см. Здесь https://developer.apple.com/videos/play/wwdc2017/712/
Примерно в 14:45 начинается обсуждение работы каналов L2Cap.
В 28:47 начинается тема Максимум использования ядра Bluetooth , в которой подробно обсуждаются вопросы, связанные с производительностью. Это, вероятно, именно то, что вас интересует.
Наконец, в 37:59 вы увидите различные возможные пропускные способности в кбит / с.
На основе данных, показанных на слайде, максимально возможная скорость с интервалом L2CAP + EDL (расширенная длина данных) + 15 мс составляет 394 кбит / с.