11.11. L2CAP-csatornák¶
A GATT egy kulcs/érték modell. Az általa kínált műveletek (read, write, notify, indicate) egyszerre egy rövid értéket mozgatnak, és a legnagyobb egyetlen hasznos teher, amelyet hordozhatnak, az, amit a megtárgyalt MTU lehetővé tesz – legjobb esetben néhány száz bájt. Ez jól működik érzékelő-leolvasásokhoz, parancsregiszterekhez és állapotjelzőkhöz. Kilobájtoknál vagy megabájtoknál összeomlik: egy hosszú folt több száz kis írásra való felbontása olyan körutakba kerül, amelyeknél a rádió sokkal gyorsabb.
A tömeges adatfolyamokhoz – egy rögzített képkocka, amelyet a kamera egy telefonra streamel, egy vezeték nélküli frissítési kép, mérések kötegelt exportja – a BLE alternatív utat kínál: a Logical Link Control and Adaptation Protocol-t, az L2CAP-ot. Az L2CAP a kapcsolati réteg és a GATT között helyezkedik el, és lehetővé teszi, hogy egy alkalmazás saját kapcsolatorientált csatornát foglaljon le ugyanazon a rádiókapcsolaton felül. A csatorna egy kredit-folyamvezérelt bájtútvonal, sokkal nagyobb csomagonkénti MTU-val és középen GATT-keretezés nélkül.
11.11.1. Mikor használj L2CAP-ot¶
Az L2CAP-csatornák a megfelelő eszköz, amikor:
Az átvitel néhány száz bájtnál nagyobb.
A két végpont mindegyike tudja, hogy egy L2CAP-csatornát fognak használni (ez nem jelenik meg a hirdetési hasznos teherben; a kliensnek a csatorna protocol/service multiplexer-, azaz PSM-számát sávon kívül kell ismernie).
Az alkalmazás hajlandó lemondani a GATT kényelmi funkcióiról: nincs beépített UUID szerinti címezhetőség, nincs kliens-felfedezhetőség standard alkalmazásokon keresztül, nincsenek értesítések.
A leggyakoribb eset az aioble-alapú alkalmazásokban egy bináris folt mozgatása két szoftverdarab között, amelyek mindketten ismerik a PSM-konvenciót – egy egyedi kamera-telefon protokoll, egy pár egymással beszélgető openmv kamera, egy belső firmware-frissítési útvonal egy peripheral GATT-szolgáltatása alatt.
Minden máshoz maradj a GATT-nál. Egy rövid állapot, egy vezérlőregiszter, egy érzékelő-leolvasás – mindezek egy karakterisztikába valók.
11.11.2. Csatorna létrehozása¶
Az L2CAP egy meglévő aioble.DeviceConnection felett fut, így a GAP-oldali felfedezési / hirdetési / csatlakozási folyamat pontosan ugyanaz, mint a GATT-nál. Miután mindkét oldal kapcsolatot tart, az egyik oldal egy PSM-en figyel, a másik oldal csatlakozik hozzá.
A PSM csupán egy kis egész szám. A Bluetooth SIG a tartomány alját szabványosított használatra tartja fenn (0x0001-0x007F); az alkalmazásspecifikus csatornákhoz használj egy számot a dinamikus tartományból (0x0080-0x00FF a rögzített PSM-ekhez, a 0x0040-től felfelé tipikusan szabad az egyedi használathoz). Mindkét oldalnak előzetesen meg kell egyeznie az értékben.
Az L2CAP-csatorna MTU-ja a legnagyobb egyetlen SDU (Service Data Unit), amelyet bármelyik oldal egyetlen send() hívásban kézbesít – nem a BLE-kapcsolat MTU-ja. Az aioble automatikusan feldarabolja a nagyobb hasznos terheket. A kamera BLE-hosztja az L2CAP MTU-t 1017 bájtban maximálja; az 512 ésszerű alapérték, amely mindkét oldalon helyet hagy anélkül, hogy RAM-ot égetne el.
A figyelő oldalon (pl. a kamera mint peripheral):
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()
A csatlakozó oldalon (pl. egy telefon vagy 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
A l2cap_accept() blokkol, amíg a társ csatlakozik (vagy a timeout_ms lejár); a l2cap_connect() blokkol, amíg a figyelő elfogadja (vagy meghiúsul). Mindkettő egy aioble.L2CAPChannel objektumot ad vissza – maga is aszinkron kontextuskezelő, amely kilépéskor lezárja a csatornát.
11.11.3. Küldés és fogadás¶
A csatorna két fő művelete a send() (bájtokat ír a társnak) és a recvinto() (egy előre lefoglalt pufferbe olvas). Mindkettő coroutine.
A
send()MTU-méretű darabokra darabolja a puffert, és közöttük megvárja a kapcsolati réteg folyamvezérlési kreditjeit. Egy hosszú küldés az alkalmazás szemszögéből egyetlenawait; belsőleg sok csomagot sorba állíthat, és szünetel, valahányszor a társ fogadási kreditjei elfogynak.A
recvinto()feltölti az átadott puffert mindazzal, ami elérhető (a csatorna MTU-jáig), és visszaadja a bájtok számát. Vár, ha semmi sem elérhető.A
available()szinkron módonTrueértéket ad vissza, ha van pufferelt, kész adat – hasznos a felfüggesztés nélküli lekérdezéshez.A
flush()vár, amíg bármely függőben lévő küldés teljesen át nem továbbítódik a vezérlőhöz.
Az L2CAP-csatornák abban az értelemben folyamszerűek, hogy a bájtok sorrendben és veszteség nélkül érkeznek, de egyetlen send határai megőrződnek – minden SDU egyetlen recvinto hívásból jön ki. Ez eltér a TCP-től, ahol egy send() határai több recv() híváson keresztül szétkenődhetnek.
11.11.4. Lekapcsolás kezelése¶
A csatorna három feltétel mellett szűnik meg: bármelyik oldal meghívja a disconnect() hívást, az alatta lévő GAP-kapcsolat megszakad, vagy megérkezik az L2CAP-szintű lekapcsolás. Az aktív műveletek aioble.L2CAPDisconnectedError kivételt váltanak ki. A GATT-oldalhoz hasonlóan ez kivételként jelenik meg a várakozó coroutine-ban, és az async with channel blokk tisztán kilép.
Ha egy csatorna egy GAP-szintű lekapcsoláson keresztül elérhetetlenné válik, az alkalmazás ugyanúgy visszatér a hirdetéshez vagy beolvasáshoz, mint egy GATT-lekapcsolás esetén.
11.11.5. Memóriaköltség¶
A nagyobb MTU-k és a hosszabb sorok több RAM-ot használnak mindkét oldalon. Egy 512 bájtos MTU plusz egy csatornánkénti fogadási puffer körülbelül 1 KB csatornánként – nem ingyenes egy kis kamerán, ha egyszerre több csatorna van nyitva. Maradj egy csatornánál kapcsolatonként, és válassz olyan MTU-t, amely megfelel a várt üzenetméretnek; az alapértelmezett egy L2CAPChannel per DeviceConnection elegendő a legtöbb alkalmazáshoz.
Az L2CAP a BLE biztonsági szelepe. A GATT az, amihez szinte minden alkalmazás először nyúl, és ennek a szakasznak a többi central / peripheral példája is a GATT-nál marad. A csatorna-ízű API a válasz, amikor egy alkalmazás kinövi a kulcs/érték modellt.