异步传输中的批量发送问题

网络传输涉及发送与接收两方面, 其中接收较为简单, 而发送却非常复杂, 通常会涉及一二个难点;


一: 异步发送时的数据乱序列问题


    如果不等上一次数据发完就进行下一次的异步发送, 无法对包的先后顺序进行控制,  所以接收端将无法解析包,
导致接收的数据没有任何意义;  针对这个现象, 有人提出弄一个队列, 向队列投递数据, 发送完队列中的一个再发
下一个; 这种方法是可以解决问题的, 但完全损害了采用异步发送的初衷, 如果这样还不如直接采用同步发送来得好;
这样还可以省一个队列; 如果采用队列, 数据又不能发送出去, 就可能产生内存的暴涨问题, 又如何解决; 

如果从socket层面来看, 这个问题是没有好的解决方法的, 但如果从我们发送的数据方面来着手, 就很容易解决了;
为了使接收端能解析接收到的乱序包, 必须为每个包的包头添加对应的标志符, 指定这个包的用处, 及收到后如何重新
组装成一个新的包; 这样我们就可以无顾忌的发送了;  但这也造成了网络资源的浪费, 因为每个包可能会多一两个字节;
一般而言, MAC 层接收的包数据大概在1400左右, 就相当于多产生了 1/700 的流量; 当然我个人觉得这个附加流量是非常
小的了. 所以这种方案最划算;

二: 异步发送时, 数据发送丢失的现象:


    有时候发送一个数据包, 可能有点大, 一次性发不完, 需要多次发送, 而异步投递有一个回调函数, 我们可以把发送的字节
及剩余的字节数,还有本次发送负责的数据块 传递进来; 这样我们在回调里就再次进行发送; 让剩下的数据发完; 当然如果怕
    乱序, 还是要加上相应标志位来传输; 通常为了保证数据发送能一次性完成, 都定义成以太网的包大小即可, (1400个字节);


    第二个难点, 其实并不难; 只是要注意, 发送的时候不要采用临时内存, 免得没发送完第二次发送时内存无效的现象发生;
### 异步批量传输的核心机制 在 `libusb` 中,异步传输机制基于事件驱动模型,通过预分配多个传输结构体(`libusb_transfer`),将它们提交到 USB 控制器,并在数据到达后通过回调函数进行处理。这种方式避免了同步传输的阻塞特性,使得应用能够持续接收或发送数据,适用于需要高吞吐量和低延迟的场景 [^1]。 ### 传输结构体的预分配与队列管理 为了实现高效的异步传输,需要预分配多个 `libusb_transfer` 结构体,并持续提交,确保 USB 控制器始终有可用的传输请求。这样可以避免因传输请求不足而导致的设备空闲,从而减少丢帧的可能性。 ```c #define NUM_XFERS 4 struct libusb_transfer *transfers[NUM_XFERS]; unsigned char *buffers[NUM_XFERS]; for (int i = 0; i < NUM_XFERS; i++) { buffers[i] = malloc(BUF_SIZE); transfers[i] = libusb_alloc_transfer(0); libusb_fill_bulk_transfer(transfers[i], dev_handle, endpoint, buffers[i], BUF_SIZE, bulk_transfer_cb, NULL, 0); libusb_submit_transfer(transfers[i]); } ``` 在回调函数中,应重新提交已完成的传输,以维持队列的持续运行: ```c void bulk_transfer_cb(struct libusb_transfer *transfer) { if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { // 处理接收到的数据 process_data(transfer->buffer, transfer->actual_length); } // 重新提交该传输 libusb_submit_transfer(transfer); } ``` ### 缓冲区大小与端点特性匹配 为了提升传输效率,缓冲区大小应与设备端点的最大包长(Max Packet Size)相匹配。可以通过 `libusb_get_max_packet_size()` 获取端点的最大包长,并据此设置合理的缓冲区大小 [^1]。 ```c int max_packet_size = libusb_get_max_packet_size(dev_handle, endpoint); ``` 例如,如果设备是全速设备(Full Speed),其端点最大包长为 64 字节,那么在进行 163 字节的数据传输时,需要分 3 个事务传输,每个事务分别使用 DATA0、DATA1、DATA0 的 PID 标记 [^3]。 ### 回调函数的轻量化处理 在异步传输中,回调函数应在最短时间内完成,避免执行耗时操作。建议在回调中仅进行数据复制或标记处理,将实际的数据处理逻辑移至其他线程或主循环中执行,防止阻塞 `libusb_handle_events` 的运行 [^1]。 ### 多线程与事件处理 在多线程环境中,应确保 `libusb_handle_events` 在一个专用线程中运行,以避免线程竞争和状态混乱。可以使用 `libusb_handle_events_timeout_completed` 来实现线程安全的事件处理机制 。 ```c while (running) { libusb_handle_events_timeout_completed(NULL, &timeout, NULL); } ``` ### 错误处理与端点重置 在回调中应检查 `transfer->status`,对错误状态进行处理。若发生错误,如 `LIBUSB_TRANSFER_ERROR` 或 `LIBUSB_TRANSFER_TIMED_OUT`,可以尝试重新提交传输或调用 `libusb_clear_halt()` 重置端点 [^1]。 ```c if (transfer->status == LIBUSB_TRANSFER_ERROR || transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { libusb_clear_halt(transfer->dev_handle, transfer->endpoint); libusb_submit_transfer(transfer); } ``` ### 性能优化与平台限制 需要注意的是,`libusb` 作为一个跨平台的通用 USB 库,在某些性能要求较高的场景下可能无法满足严格的实时性需求。例如在高速批量传输时,由于平台抽象层的限制,`libusb` 的传输效率可能不如原生驱动或专用库 [^2]。因此,在设计系统时应评估其性能边界,并在必要时结合硬件加速或专用固件进行优化。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值