Skip to content

Channels

An RfcommChannel carries bytes in both directions. You get one by connecting to a device or by accepting a connection. Both kinds work the same way.

Sending and receiving

send sends all the bytes you give it. It returns once they are on their way, which does not mean the other device has them yet. receive waits until some bytes arrive and returns them:

await channel.send(b"hello\n")

try:
    while True:
        print((await channel.receive()).decode(), end="")
except ChannelClosedError as error:
    print(f"\nthe connection ended: {error}")

receive never returns empty bytes. When the connection ends, receive first returns any bytes that arrived before the end. After that, every call to receive raises ChannelClosedError, and so does every call to send. That is what ends the loop above.

One task can send while another task receives. Two tasks receiving at the same time, or two tasks sending, is a bug, and raises BusyResourceError.

Reading messages

RFCOMM carries a stream of bytes, not separate messages. One send on the other end may arrive in several pieces, or joined to the next message. To read messages, your program has to know where each one ends.

For text, ending each message with a newline is the simplest way. receive_until reads up to a delimiter and returns the bytes before it:

line = await channel.receive_until(b"\n", max_bytes=1024)

The delimiter is read too, but not returned.

max_bytes is the longest message you expect, delimiter included. It is required, so that a device that never sends a delimiter cannot make your program store more and more. As soon as max_bytes bytes have arrived with no delimiter among them, receive_until raises DelimiterNotFoundError. It reads none of those bytes, so calling it again raises again. Read them another way, for example with receive, or end the connection:

try:
    line = await channel.receive_until(b"\n", max_bytes=1024)
except DelimiterNotFoundError:
    # Too long to be a line. Take the first part as it is.
    line = await channel.receive(1024)

For binary data, a common way is to send each message's length first. receive_exactly reads an exact number of bytes:

async def read_message(channel) -> bytes:
    (length,) = struct.unpack("<H", await channel.receive_exactly(2))
    return await channel.receive_exactly(length)

You can mix receive, receive_until and receive_exactly. Bytes that one call does not use are kept for the next, and canceling a call loses no bytes. If the connection ends partway through a message, receive_until and receive_exactly raise ChannelClosedError, and receive then returns the part that arrived.

receive(max_bytes) does not wait for that many

It returns at most max_bytes, as soon as anything has arrived. Use receive_exactly when you need an exact number of bytes.

Next: Lifetimes, to notice when a connection ends.