While trying to set a data transfer in CoreBluetooth
using L2CAP
channel.
I am having problems. On the sending side I have this function firing on the tap of a button:
@objc func sendL2CAPInfo() {
print(#function)
let value = "Hello L2Cap Stream data...",
data = Data(value.utf8)
outPutStream.open()
print("outPutStream -> \(outPutStream.debugDescription)")
let bytesWritten = data.withUnsafeBytes { outPutStream.write($0, maxLength: data.count) }
print("bytesWritten = \(bytesWritten)")
}
When it is executed I see this in the Xcode debugging console:
sendL2CAPInfo()
outPutStream -> Optional(<__NSCFOutputStream: 0x282969b00>)
bytesWritten = 26
On the receiving side I have this function, also firing on the tap of a button:
@objc func receiveL2CAPInfo() {
print(#function)
let bufLength = 10
var buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufLength)
inPutStream.open()
let bytesRead = inPutStream.read(buffer, maxLength: bufLength)
print("bytesRead = \(bytesRead)")
}
Still on the receiving side I also have this code:
// StreamDelegate protocol implementation.
func stream(_ aStream: Stream,
handle eventCode: Stream.Event) {
print(#function)
if aStream == inPutStream {print("Input-Stream")}
if aStream == outPutStream {print("Output-Stream")}
switch eventCode {
case Stream.Event.hasBytesAvailable:
print("Stream.Event.hasBytesAvailable")
receiveL2CAPInfo()
case Stream.Event.hasSpaceAvailable:
print("Stream.Event.hasSpaceAvailable")
case Stream.Event.openCompleted:
print("Stream.Event.openCompleted")
case Stream.Event.endEncountered:
print("Stream.Event.endEncountered")
case Stream.Event.errorOccurred:
print("Stream.Event.errorOccurred")
default:
print("Stream.Event.SOME-OTHER-THING-HAPPENED")
}
}
When launching the receiving app (while the sending app is running) I see this in the Xcode debugging console:
centralManagerDidUpdateState
centralManager(_:didDiscover:advertisementData:rssi:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-17 13:16:37.168700+0900 CBL2CAPCh_Central[857:127305] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Input-Stream
Stream.Event.hasBytesAvailable
receiveL2CAPInfo()
bytesRead = 0
stream(_:handle:)
Input-Stream
Stream.Event.endEncountered
On the sending side, it seems that things go as expected, 26 bytes are written. On the other hand on the receiving side we do not see the 26 bytes coming in.
What is wrong? I have never used L2CAP
channel before, so I must certainly be doing something not good, but I have no idea what. Any recommendation on what to check or where to look at would be very much appreciated.
..... After more digging into the subject, here is some more information.
Here is the code for for the stream(_ :handle:) function:
func stream(_ aStream: Stream,
handle eventCode: Stream.Event) {
print(#function)
if aStream == inPutStream {print("Input-Stream")}
if aStream == outPutStream {print("Output-Stream")}
if (aStream != inPutStream) && (aStream != outPutStream)
{print("Some-other-Stream")}
switch eventCode {
case Stream.Event.hasBytesAvailable:
print("Stream.Event.hasBytesAvailable")
case Stream.Event.hasSpaceAvailable:
print("Stream.Event.hasSpaceAvailable")
case Stream.Event.openCompleted:
print("Stream.Event.openCompleted")
case Stream.Event.endEncountered:
print("Stream.Event.endEncountered")
case Stream.Event.errorOccurred:
print("Stream.Event.errorOccurred")
if let error = aStream.streamError {
print("Error:\n\t\(error.localizedDescription)")
}
default:
print("Stream.Event.SOME-OTHER-THING-HAPPENED")
}
}
Here is what can be seen in the debugger once the central app is started, before attempting to write anything:
centralManagerDidUpdateState
centralManager(_:didDiscover:advertisementData:rssi:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:32:14.840892+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Input-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Output-Stream
Stream.Event.hasSpaceAvailable
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:32:28.521320+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Input-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Output-Stream
Stream.Event.hasSpaceAvailable
2019-01-18 13:32:28.523746+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:32:38.948090+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
2019-01-18 13:32:38.949889+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:32:49.278142+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
2019-01-18 13:32:49.280093+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:32:59.739338+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
2019-01-18 13:32:59.741111+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:33:10.567965+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
2019-01-18 13:33:10.569722+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:33:21.404162+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
2019-01-18 13:33:21.406243+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:33:32.147567+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
2019-01-18 13:33:32.148884+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:33:42.788001+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
2019-01-18 13:33:42.789921+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
2019-01-18 13:33:53.734140+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 431
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
2019-01-18 13:33:53.736063+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] No known channel matching peer <CBPeripheral: 0x281bc4a00, identifier = D73B08A0-E9BB-483B-8078-4A7F992E7565, name = Michel’s iPad, state = connected> with psm 192
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
2019-01-18 13:34:04.231561+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 436
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
2019-01-18 13:34:14.861939+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 436
centralManager(_:didDisconnectPeripheral:error:)
centralManager(_:didConnect:)
peripheral(_:didOpen:error:)
stream(_:handle:)
Some-other-Stream
Stream.Event.hasSpaceAvailable
stream(_:handle:)
Output-Stream
Stream.Event.openCompleted
stream(_:handle:)
Input-Stream
Stream.Event.openCompleted
stream(_:handle:)
Some-other-Stream
Stream.Event.hasBytesAvailable
2019-01-18 13:34:25.472075+0900 CBL2CAPCh_Central[571:80355] [CoreBluetooth] WARNING: Unknown error: 436
Rather than attempting to open the stream when a button is tapped, you should open the stream when the channel is connected. You can then use the stream delegate hasBytesAvailable
event to determine that there are bytes to read.
It is important that you schedule the streams on the current run loop.
For example, to open the streams on a CBCentral
:
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?) {
if let error = error {
print("Error opening l2cap channel - \(error.localizedDescription)")
return
}
guard let channel = channel else {
return
}
print("Opened channel \(channel)")
self.channel = channel
channel.inputStream.delegate = self
channel.outputStream.delegate = self
print("Opened channel \(channel)")
channel.inputStream.schedule(in: RunLoop.current, forMode: .default)
channel.outputStream.schedule(in: RunLoop.current, forMode: .default)
channel.inputStream.open()
channel.outputStream.open()
}
And then to handle the received data:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.openCompleted:
print("Stream is open")
case Stream.Event.endEncountered:
print("End Encountered")
case Stream.Event.hasBytesAvailable:
print("Bytes are available")
if let iStream = aStream as? InputStream {
let bufLength = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufLength)
let bytesRead = iStream.read(buffer, maxLength: bufLength)
print("bytesRead = \(bytesRead)")
if let string = String(bytesNoCopy: buffer, length: bytesRead, encoding: .utf8, freeWhenDone: false) {
print("Received data: \(string)")
}
}
case Stream.Event.hasSpaceAvailable:
print("Space is available")
case Stream.Event.errorOccurred:
print("Stream error")
default:
print("Unknown stream event")
}
}
My full working example is available here: https://github.com/paulw11/L2CapDemo