Bluez/Python 缓冲导致蓝牙连接延迟

Posted

技术标签:

【中文标题】Bluez/Python 缓冲导致蓝牙连接延迟【英文标题】:Bluez/Python buffering causes laggy bluetooth connection 【发布时间】:2021-04-20 20:08:56 【问题描述】:

我编写了一个 Python 程序,它通过蓝牙套接字(参见下面的示例)读取和写入 L2CAP 连接以与某些硬件通信。但是,代码可能希望为蓝牙适配器处理太多的 IO,因此我将其配备为通过在需要时减慢 bar 循环来处理滞后的读取和写入,因为这比在响应请求时延迟更可取。

sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
sock.connect((bt_addr, bt_port))
sock.setblocking(False)

stall_loop = asyncio.Event()
stall_loop.set()

async def bar():
    while True:
        await stall_loop.wait()
        await loop.sock_sendall(generate_data(time.time()))
        await asyncio.sleep(maybe_to_small_delay)

async def foo():
    while True:
        await request = loop.sock_recv(sock, 400)
        stall_loop.clear()
        await loop.sock_sendall(generate_response(request))
        stall_loop.set()

asyncio.gather(foo, bar)
        

但问题是,在bar 一次冻结几秒钟之前,这些循环都没有看到任何明显的减速。

我使用time.time() 对两个循环的执行进行计时,执行时间只有毫秒,包括 IO(bar 冻结时除外),但foo 写入的任何内容都只能延迟 15 秒发送(由tshark 报告并与python 认为写入完成的时间进行比较)

我想要的行为是仅在实际传输数据后才返回写入,以便保持响应 foobar 尽可能接近所需速度。

我认为罪魁祸首是某种缓冲策略,它会缓冲大量数据,直到缓冲区已满,然后将其完全刷新。我怎样才能禁用此 bahviour 和/或将缓冲区大小限制为例如2包?我在 ioctl 上找不到 bluez 的文档。

版本:

内核 5.10.17-vl7+ 上的 Raspbian Python 3.7.3 bluez 5.50

【问题讨论】:

@ukBaz,我想到了,但遗憾的是他们不支持蓝牙插座。它们都是 Opening Network Connection API 的包装器,仅支持 AF_INETAF_INET6AF_UNIX 使用附加功能。 @ukBaz 所以你的意思是我应该像上面那样创建套接字,然后用一个小缓冲区将它包装在 Stream 中。这不会造成更多的延迟,因为套接字本身已经滞后并且 Stream 缓冲区也是如此,或者 Streams 是否对我不了解的套接字有一些了解并配置套接字的缓冲? 【参考方案1】:

问题在于 Linux 中的缓冲,解决方案是禁用它:

client_itr.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 0)

今天在GNU libc docs 中找到了这个选项。

我不知道为什么它决定在缓冲区填满后完全刷新缓冲区(这导致bar-loop 冻结了一段时间),但没有缓冲行为是预期的:foo 接收之间即使没有Event-locking,一个请求和它发送的回复最多也有2个bar数据包。

但是请注意,如果不缓冲sendall,则只会阻塞直到数据通过蓝牙发送,而不是直到接收方确认。为此,您可能必须降低 L2CAP 的窗口大小。

【讨论】:

以上是关于Bluez/Python 缓冲导致蓝牙连接延迟的主要内容,如果未能解决你的问题,请参考以下文章

Android蓝牙:软件导致连接中止IOException? [复制]

iPhone 上的最大蓝牙连接数是多少?

蓝牙延迟和 Android 音频提示

ps4手柄连接强度太低

垃圾收集导致连接的套接字延迟(NodeJS 服务器)

iOS 蓝牙检索外设导致 EXC_BAD_ACCESS 崩溃