11.11. L2CAP kanalları

GATT bir anahtar/değer modelidir. Sunduğu işlemler (okuma, yazma, bildirim, gösterim) tek seferde bir kısa değer taşır ve taşıyabilecekleri en büyük tekil yük, anlaşılan MTU’nun izin verdiği kadardır – en iyi durumda birkaç yüz bayt. Bu, sensör okumaları, komut yazmaçları ve durum bayrakları için iyi çalışır. Kilobaytlarda veya megabaytlarda dağılır: uzun bir nokta (blob), yüzlerce küçük yazma işlemine bölündüğünde, radyonun çok daha hızlı olduğu gidiş-dönüşlere mal olur.

Toplu veri akışları için – kameranın bir telefona aktardığı yakalanmış bir çerçeve, kablosuz bir güncelleme görüntüsü, gruplanmış bir ölçüm dışa aktarımı – BLE alternatif bir yol sunar: Mantıksal Bağlantı Denetimi ve Uyarlama Protokolü, L2CAP. L2CAP, bağlantı katmanı ile GATT arasında yer alır ve bir uygulamanın aynı radyo bağlantısı üzerinde kendi bağlantı yönelimli kanalını talep etmesine olanak tanır. Kanal, çok daha büyük bir paket başına MTU’ya sahip ve ortada GATT çerçevelemesi olmayan, kredi akışıyla denetlenen bir bayt yoludur.

11.11.1. L2CAP ne zaman kullanılır

L2CAP kanalları şu durumlarda doğru araçtır:

  • Aktarım birkaç yüz bayttan fazladır.

  • İki uç da bir L2CAP kanalının kullanılacağını bilir (bu, yayın yükünde açığa çıkmaz; istemcinin kanalın protokol/hizmet çoklayıcısı yani PSM numarasını bant dışından bilmesi gerekir).

  • Uygulama, GATT inceliklerinden vazgeçmeye razıdır: UUID ile yerleşik adreslenebilirlik yok, standart uygulamalar aracılığıyla istemci tarafından keşfedilebilirlik yok, bildirim yok.

aioble tabanlı uygulamalardaki en yaygın durum, PSM kuralını bilen iki yazılım parçası arasında ikili bir nokta (blob) taşımaktır – özel bir kamera-telefon protokolü, birbiriyle konuşan bir çift openmv kamerası, bir peripheral’ın GATT hizmeti altındaki dahili bir aygıt yazılımı güncelleme yolu.

Diğer her şey için GATT’ta kalın. Kısa bir durum, bir denetim yazmacı, bir sensör okuması – bunların hepsi bir karakteristikte yer alır.

11.11.2. Bir kanal kurma

L2CAP, mevcut bir aioble.DeviceConnection üzerinde çalışır, dolayısıyla GAP tarafındaki keşif / yayın / bağlanma akışı GATT için olanla tamamen aynıdır. Her iki taraf da bir bağlantıya sahip olduğunda, bir taraf bir PSM üzerinde dinler, diğer taraf ona bağlanır.

PSM yalnızca küçük bir tam sayıdır. Bluetooth SIG, aralığın alt kısmını standartlaştırılmış kullanım için ayırır (0x0001-0x007F); uygulamaya özgü kanallar için dinamik aralıktan bir numara kullanın (sabit PSM’ler için 0x0080-0x00FF, özel kullanım için tipik olarak 0x0040 ve sonrası serbesttir). Her iki tarafın da değer üzerinde önceden anlaşması gerekir.

Bir L2CAP kanalındaki MTU, her iki tarafın da tek bir send() çağrısında teslim edeceği en büyük tekil SDU’dur (Hizmet Veri Birimi) – BLE bağlantı MTU’su değil. Aioble, daha büyük yükleri otomatik olarak parçalar. Kameranın BLE ana bilgisayarı L2CAP MTU’sunu 1017 baytla sınırlar; 512, RAM tüketmeden her iki tarafta da yer bırakan makul bir varsayılandır.

Dinleyici tarafında (ör. peripheral olarak kamera):

async def serve_l2cap(connection, image_bytes):
    channel = await connection.l2cap_accept(psm=0x80, mtu=512)
    async with channel:
        # image_bytes is a bytearray -- e.g. csi0.snapshot().bytearray()
        # or a compressed JPEG buffer. send() fragments into MTU-sized
        # chunks automatically and awaits flow-control credits between.
        await channel.send(image_bytes)
        await channel.flush()

Bağlayıcı tarafında (ör. bir telefon veya central):

async def open_l2cap(connection, total_bytes):
    channel = await connection.l2cap_connect(psm=0x80, mtu=512)
    async with channel:
        image_bytes = bytearray(total_bytes)
        view = memoryview(image_bytes)
        received = 0
        while received < total_bytes:
            n = await channel.recvinto(view[received:])
            if n == 0:
                break
            received += n
        return image_bytes

l2cap_accept(), eş bağlanana kadar (veya timeout_ms tetiklenene kadar) bloke eder; l2cap_connect(), dinleyici kabul edene (veya başarısız olana) kadar bloke eder. Her ikisi de bir aioble.L2CAPChannel döndürür – bu, çıkışta kanalı kapatan bir asenkron bağlam yöneticisinin kendisidir.

11.11.3. Gönderme ve alma

Bir kanaldaki iki ana işlem send() (eşe bayt yazar) ve recvinto() (önceden ayrılmış bir arabelleğe okur) işlemleridir. Her ikisi de eşyordamdır.

  • send(), arabelleği MTU boyutunda parçalara böler ve aralarında bağlantı katmanı akış denetimi kredilerini bekler. Uzun bir gönderim, uygulamanın bakış açısından tek bir await çağrısıdır; dahili olarak birçok paketi kuyruğa alabilir ve eşin alım kredileri tükendiğinde duraklayabilir.

  • recvinto(), geçirilen arabelleği mevcut olan her şeyle (kanalın MTU’suna kadar) doldurur ve bayt sayısını döndürür. Hiçbir şey mevcut değilse bekler.

  • available(), hazır arabelleğe alınmış veri varsa eşzamanlı olarak True döndürür – askıya almadan yoklama yapmak için yararlıdır.

  • flush(), bekleyen herhangi bir gönderim denetleyiciye tamamen iletilene kadar bekler.

L2CAP kanalları, baytların sıralı ve kayıpsız bir şekilde ulaşması anlamında akış benzeridir, ancak tek bir send işleminin sınırları korunur – her SDU tek bir recvinto çağrısından çıkar. Bu, bir send() çağrısının sınırlarının birden fazla recv() çağrısına yayılabildiği TCP’den farklıdır.

11.11.4. Bağlantı kesme işlemi

Kanal üç koşulda ortadan kalkar: iki taraftan biri disconnect() çağırır, alttaki GAP bağlantısı düşer veya L2CAP düzeyinde bir bağlantı kesme gelir. Etkin işlemler aioble.L2CAPDisconnectedError hatası fırlatır. GATT tarafında olduğu gibi, bu, bekleyen eşyordamda bir özel durum olarak ortaya çıkar ve async with channel bloğu temiz bir şekilde çıkar.

Bir kanal, GAP düzeyinde bir bağlantı kesme nedeniyle erişilemez hale gelirse, uygulama bir GATT bağlantı kesmesinde yapacağı gibi yayına veya taramaya geri döner.

11.11.5. Bellek maliyeti

Daha büyük MTU’lar ve daha uzun kuyruklar her iki tarafta da daha fazla RAM kullanır. 512 baytlık bir MTU ile kanal başına bir alım arabelleği, kanal başına yaklaşık 1 KB’dir – aynı anda birkaç kanal açıksa küçük bir kamerada ücretsiz değildir. Bağlantı başına tek bir kanalda kalın ve beklenen mesaj boyutuna uygun bir MTU seçin; DeviceConnection başına bir L2CAPChannel varsayılanı çoğu uygulama için yeterlidir.

L2CAP, BLE’nin emniyet supabıdır. GATT, neredeyse her uygulamanın ilk başvurduğu şeydir ve bu bölümün geri kalan central / peripheral örnekleri GATT’ta kalır. Kanal türündeki API, bir uygulama anahtar/değer modelini aştığında verilen yanıttır.