试图了解 L2CAP 通道

Trying to understand L2CAP channel

现在使用 CoreBlueTooth L2CAP channel 面临一些挑战。为了更好地理解事物是如何工作的。我从 GitHub 获取了 L2CapDemo (master) (https://github.com/paulw11/L2CapDemo) 并尝试使用它进行实验。这是我所做的,还有一个问题。

用这个替换了 sendTextTapped 函数:

@IBAction func sendTextTapped(_ sender: UIButton) {
    guard let ostream = self.channel?.outputStream else {
        return
    }

    var lngStr = "1234567890"
    for _ in 1...10 {lngStr = lngStr + lngStr}
    let data = lngStr.data(using: .utf8)!

    let bytesWritten =  data.withUnsafeBytes { ostream.write([=10=], maxLength: data.count) }
    print("bytesWritten = \(bytesWritten)")
    print("WR = \(bytesWritten) / \(data.count)")
}

执行结果为:

bytesWritten = 8192
WR = 8192 / 10240

这让我可以看到在 bytesWritten < data.count 的情况下会发生什么。 换句话说,所有的字节不能在一个块中发送。

问题来了。问题是我什么也没看到,剩下的字节似乎被忽略了。 如果我不想忽略这些字节,我想知道该怎么做。关心其余字节的方法是什么?会有一些情况我们需要传输几万甚至几十万字节。

您只需记下发送了多少个字符,从 data 实例中删除这些字符,然后当您收到一个指示 space 在输出流中可用的委托回调时,再发一些。

例如,您可以添加几个属性来保存排队的数据和一个串行调度队列以确保对该队列的线程安全访问:

private var queueQueue = DispatchQueue(label: "queue queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem, target: nil)

private var outputData = Data()

现在,在 sendTextTapped 函数中,您只需将新数据添加到输出队列即可:

@IBAction func sendTextTapped(_ sender: UIButton) {     
    var lngStr = "1234567890"
    for _ in 1...10 {lngStr = lngStr + lngStr}
    let data = lngStr.data(using: .utf8)!

    self.queue(data:data)  
}

queue(data:)函数以线程安全的方式将数据添加到outputData对象并调用send()

private func queue(data: Data) {
    queueQueue.sync  {
        self.outputData.append(data)
    }
    self.send()   
}

send() 确保流已连接,有数据要发送并且输出流中有 space 可用。如果一切正常,那么它会发送尽可能多的字节。然后从输出中删除发送的字节 data(再次以线程安全的方式)。

private func send() {

    guard let ostream = self.channel?.outputStream, !self.outputData.isEmpty, ostream.hasSpaceAvailable  else{
        return
    }
    let bytesWritten =  outputData.withUnsafeBytes { ostream.write([=13=], maxLength: self.outputData.count) }
    print("bytesWritten = \(bytesWritten)")
    queueQueue.sync {
        if bytesWritten < outputData.count {
            outputData = outputData.advanced(by: bytesWritten)
        } else {
            outputData.removeAll()
        }
    }
}

最后的更改是调用 send() 以响应 .hasSpaceAvailable 流事件:

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")
    case Stream.Event.hasSpaceAvailable:
        print("Space is available")
        self.send()
    case Stream.Event.errorOccurred:
        print("Stream error")
    default:
        print("Unknown stream event")
    }
}

在示例的largedata分支可以看到修改后的代码