libusb_bulk_transfer传输接收数据过长 零长度包修复无效问题咨询
待解决的问题
我使用libusb_bulk_transfer通过USB传输数据,代码示例如下:
int sent = 0; int bulk_result = libusb_bulk_transfer(handle, endpoint_out->bEndpointAddress, buffer, buffer_len, &sent, 5000);
另一端使用安卓平台的Kotlin代码接收传输数据。
大多数时候运行正常:比如发送289字节的缓冲区,接收端也会收到289字节。
但偶尔会出现接收数据量过大的问题:比如发送1536字节,实际收到1719字节。
我的无效修复方案
我查阅到的资料指出:「批量传输完成的判定条件为:传输了请求的 exact 数据量、传输了小于端点最大包长的数据包,或传输了零长度包」。
由于1536是64的倍数,且所有异常接收的数据包长度都是64的倍数,我判断问题源于此,因此计划在发送长度为端点最大包长整数倍的缓冲区后,追加发送一个零长度包。我也注意到端点最大包长不一定是64,因此做了动态获取处理。
我的「修复」代码如下:
int sent = 0; int bulk_result = libusb_bulk_transfer(handle, endpoint_out->bEndpointAddress, buffer, buffer_len, &sent, 5000); if (sent % get_usb_packet_size() == 0) { libusb_bulk_transfer(handle, endpoint_out->bEndpointAddress, nullptr, 0, &sent, 5000); }
get_usb_packet_size()的实现如下,返回值为256:
int get_usb_packet_size() { return endpoint_out->wMaxPacketSize; }
但该方案并未生效:两次libusb_bulk_transfer的返回码都是0(成功),第一次调用上报发送了预期的buffer_len字节,第二次上报发送了0字节,均符合预期。
但接收端仍然会收到超出预期长度的数据包。我尝试把判断阈值换成64以发送更多零长度包,问题依旧存在。请问我遗漏了什么?
解答
你遗漏的核心问题包括以下几点:
- 安卓端接收缓冲区复用问题:安卓USB bulkTransfer接收接口不会主动清空传入的缓冲区,如果你重复使用同一个字节数组接收数据,之前残留的旧数据会和新接收的数据混在一起,导致你统计到的长度超过实际接收长度。你需要每次调用接收接口前将缓冲区全部置零,接收后仅保留接口返回值长度的有效数据,不要统计整个缓冲区的大小。
- 应用层缺少传输边界协议:仅靠USB底层的零长度包做边界不可靠,部分安卓底层驱动不会将零长度包作为前序传输的结束标记,反而会将其当作独立帧处理。你可以在应用层给每个传输块加固定长度的头部,比如前4字节存储本次传输的有效数据长度,接收端先读取头部拿到长度,再严格按照该长度读取后续数据,彻底规避底层传输边界的影响。
- 零长度包发送逻辑有缺陷:你当前仅判断了实际发送字节数对包长取模为0的情况,但未校验
sent是否等于本次要发送的buffer_len。如果出现短发送的情况,你追加的零长度包会被当作错误的边界标记,反而加剧数据错乱。需要先确认sent == buffer_len之后,再判断是否需要发送零长度包。 - 安卓端接收参数不匹配:如果安卓端接收的超时参数设置过短,单次接收没拿到全量数据就返回,你如果把多次接收的结果直接拼接,也会出现长度超标的情况。可以先将安卓端接收超时设置和发送端的5000ms对齐,排除超时导致的多次接收拼接问题。
内容的提问来源于stack exchange,提问作者JonasVautherin
相关产品推荐
相关产品推荐

