JS `WebUSB` `Control Transfers` / `Bulk Transfers` / `Interrupt Transfers` 效率对比

各位技术控,大家好!我是你们的老朋友,今天咱们来聊聊 WebUSB 里的三大金刚:控制传输 (Control Transfers)、批量传输 (Bulk Transfers) 和中断传输 (Interrupt Transfers)。这三个家伙各有神通,用对了能让你的 USB 设备在 Web 应用里跑得飞起,用错了嘛…那就只能对着控制台挠头了。

咱们今天就来扒一扒它们的底裤,看看谁才是效率之王,以及在什么场景下最能发挥实力。

首先,打个招呼:嘿,USB 小伙伴们,准备好了吗? Let’s rock!

一、WebUSB 传输类型概览:谁是你的菜?

在 WebUSB 的世界里,和 USB 设备通信就像跟人打交道一样,得讲究策略。不同的传输类型就像不同的沟通方式,适合不同的场合。

传输类型 适用场景 效率特点 延迟特点
控制传输 (Control Transfers) 设备配置、获取设备信息、设置设备参数等。简单来说,就是“老板”发号施令,或者问“员工”要报告。 效率最低,但可靠性最高。每次传输都有确认机制,保证数据正确到达。 延迟最高,因为需要确认和重试机制。
批量传输 (Bulk Transfers) 大量数据传输,比如文件传输、固件升级等。就像搬运工,一次搬很多东西。 效率较高,但没有保证的带宽。如果总线繁忙,可能会被延迟。 延迟不确定,取决于总线负载。
中断传输 (Interrupt Transfers) 周期性的小数据传输,比如鼠标移动、键盘输入等。就像心跳,定时汇报情况。 效率中等,有保证的带宽,但带宽通常较小。 延迟较低,保证在一定时间内传输。

二、控制传输 (Control Transfers):老大哥,稳如泰山

控制传输就像 USB 设备的大脑,负责指挥和协调。它主要用于:

  • 设备配置: 初始化设备,设置各种参数。
  • 获取设备信息: 读取设备的描述符,了解设备的能力。
  • 控制设备行为: 发送命令,控制设备的动作。

代码示例:获取设备描述符

async function getDeviceDescriptor(device) {
  try {
    const setup = {
      requestType: 'standard',
      recipient: 'device',
      request: 0x06, // GET_DESCRIPTOR
      value: (0x01 << 8) | 0x00, // Device Descriptor, index 0
      index: 0x00,
      length: 18 // Device descriptor is 18 bytes
    };

    const result = await device.controlTransferIn(setup, 18);

    if (result.status === 'ok') {
      const descriptor = new DataView(result.data.buffer);
      console.log('Device Descriptor:', descriptor);
      return descriptor;
    } else {
      console.error('Failed to get device descriptor:', result.status);
      return null;
    }
  } catch (error) {
    console.error('Error getting device descriptor:', error);
    return null;
  }
}

代码解释:

  1. setup 对象定义了控制传输的参数:
    • requestType: 标准请求 (standard)。
    • recipient: 目标是设备 (device)。
    • request: 0x06 代表 GET_DESCRIPTOR 命令。
    • value: 指定描述符的类型和索引。这里是设备描述符 (0x01),索引为 0。
    • index: 通常为 0。
    • length: 期望读取的字节数。
  2. device.controlTransferIn(setup, 18) 发起控制传输,读取 18 字节的数据。
  3. result.status 检查传输状态,result.data 包含读取到的数据。

控制传输的优缺点:

  • 优点: 可靠性高,有错误重试机制。
  • 缺点: 效率最低,因为每次传输都需要确认,开销大。

适用场景: 不追求速度,但要求数据绝对可靠的场合,比如设备初始化、配置等。

三、批量传输 (Bulk Transfers):大力士,搬运数据一把好手

批量传输就像 USB 设备的搬运工,专门用来传输大量数据,比如:

  • 文件传输: 上传或下载文件。
  • 固件升级: 将新的固件写入设备。
  • 打印数据: 将打印数据发送到打印机。

代码示例:发送大量数据

async function sendBulkData(device, endpointNumber, data) {
  try {
    const result = await device.transferOut(endpointNumber, data);

    if (result.status === 'ok') {
      console.log('Bulk transfer successful.');
    } else {
      console.error('Bulk transfer failed:', result.status);
    }
  } catch (error) {
    console.error('Error sending bulk data:', error);
  }
}

代码解释:

  1. endpointNumber 指定批量传输的端点号码。
  2. data 是要发送的 ArrayBufferTypedArray 数据。
  3. device.transferOut(endpointNumber, data) 发起批量输出传输。
  4. result.status 检查传输状态。

批量传输的优缺点:

  • 优点: 效率较高,适合大量数据传输。
  • 缺点: 没有保证的带宽,容易受到总线负载的影响。 如果总线繁忙,批量传输可能会被延迟。

适用场景: 对实时性要求不高,但需要传输大量数据的场合,比如文件传输、固件升级等。

四、中断传输 (Interrupt Transfers):报信员,及时汇报情况

中断传输就像 USB 设备的报信员,专门用来周期性地传输小量数据,比如:

  • 鼠标移动: 报告鼠标的坐标变化。
  • 键盘输入: 报告按键事件。
  • 传感器数据: 报告传感器读数。

代码示例:接收中断数据

async function receiveInterruptData(device, endpointNumber) {
  try {
    const result = await device.transferIn(endpointNumber, 64); // Assuming 64 bytes is the maximum packet size

    if (result.status === 'ok') {
      const data = new DataView(result.data.buffer);
      console.log('Interrupt data received:', data);
      return data;
    } else {
      console.error('Interrupt transfer failed:', result.status);
      return null;
    }
  } catch (error) {
    console.error('Error receiving interrupt data:', error);
    return null;
  }
}

代码解释:

  1. endpointNumber 指定中断传输的端点号码。
  2. 64 是期望接收的最大字节数。
  3. device.transferIn(endpointNumber, 64) 发起中断输入传输。
  4. result.status 检查传输状态,result.data 包含接收到的数据。

中断传输的优缺点:

  • 优点: 有保证的带宽,延迟较低,适合实时性要求高的场合。
  • 缺点: 带宽通常较小,不适合传输大量数据。

适用场景: 对实时性要求高,需要周期性传输小量数据的场合,比如鼠标、键盘、传感器等。

五、效率对比:谁是真正的速度之王?

好了,说了这么多,咱们来个总结性的效率对比:

特性 控制传输 批量传输 中断传输
效率 最低 较高 中等
延迟 最高 不确定,取决于总线负载 较低,保证在一定时间内传输
带宽 最小 较大,但没有保证 较小,但有保证
可靠性 最高,有错误重试机制 较低,没有错误重试机制 中等,可能有错误重试机制,取决于设备实现
适用场景 设备配置、获取信息、控制设备行为 大量数据传输,文件传输、固件升级等 周期性小数据传输,鼠标、键盘、传感器等
拥塞处理 总线拥塞时会等待,可能导致传输延迟 总线拥塞时会被延迟,甚至放弃传输 总线拥塞时会降低传输频率,保证一定带宽

六、代码示例:综合应用

光说不练假把式,咱们来个综合应用,模拟一个简单的 USB 温度传感器:

  1. 设备初始化 (控制传输): 读取设备描述符,配置传感器。
  2. 读取温度数据 (中断传输): 周期性地读取温度数据。
  3. 上传数据日志 (批量传输): 将一段时间的温度数据上传到服务器。
async function temperatureSensorExample(device) {
  // 1. 设备初始化 (控制传输)
  const descriptor = await getDeviceDescriptor(device);
  if (!descriptor) {
    console.error('Failed to initialize device.');
    return;
  }
  console.log('Device initialized successfully.');

  // 2. 读取温度数据 (中断传输)
  const interruptEndpoint = 1; // 假设中断端点是 1
  let temperatureData = [];

  const readTemperature = async () => {
    const data = await receiveInterruptData(device, interruptEndpoint);
    if (data) {
      const temperature = data.getInt8(0); // 假设温度数据在第一个字节
      console.log('Temperature:', temperature);
      temperatureData.push(temperature);

      // 3. 上传数据日志 (批量传输) - 每 10 次读取后上传
      if (temperatureData.length >= 10) {
        const bulkEndpoint = 2; // 假设批量端点是 2
        const dataToUpload = new Uint8Array(temperatureData);
        await sendBulkData(device, bulkEndpoint, dataToUpload);
        temperatureData = []; // 清空数据
        console.log('Temperature data uploaded.');
      }
    }
    setTimeout(readTemperature, 100); // 每 100ms 读取一次
  };

  readTemperature();
}

代码解释:

  1. getDeviceDescriptor (控制传输) 用于读取设备描述符,初始化设备。
  2. receiveInterruptData (中断传输) 周期性地读取温度数据。
  3. sendBulkData (批量传输) 每隔一段时间将温度数据上传到服务器。

七、总结:选择最适合你的传输类型

好了,各位小伙伴,今天的 WebUSB 三大金刚讲座就到这里了。记住,没有最好的传输类型,只有最适合你的传输类型。

  • 控制传输: 适合对可靠性要求高,但对速度要求不高的场合。
  • 批量传输: 适合传输大量数据,但对实时性要求不高的场合。
  • 中断传输: 适合对实时性要求高,需要周期性传输小量数据的场合。

希望今天的分享能帮助大家更好地理解 WebUSB 的传输类型,并在实际应用中选择最合适的方案。

最后,记住:技术的世界,没有绝对的对错,只有不断学习和尝试。 祝大家玩得开心!

八、一些额外的小提示

  • 端点 (Endpoint) 的选择: USB 设备通常有多个端点,每个端点负责不同的功能。在进行传输之前,需要先了解设备的端点配置,选择正确的端点进行通信。 设备描述符会告诉你端点的信息。
  • 数据格式: WebUSB 使用 ArrayBufferTypedArray 来表示数据。在发送和接收数据时,需要注意数据的格式和编码方式。
  • 错误处理: WebUSB 传输可能会出现各种错误,比如设备断开连接、传输超时等。 需要完善的错误处理机制,保证应用的稳定性。 try...catch 和检查 result.status 是关键。
  • 权限问题: WebUSB 需要用户授权才能访问 USB 设备。 在连接设备之前,需要先请求用户授权。 navigator.usb.requestDevice() 会弹出授权窗口。
  • 调试工具: Chrome 浏览器的 chrome://inspect/#devices 可以用来调试 WebUSB 应用。 可以查看 USB 设备的连接状态、端点配置等信息。 另外,Wireshark 配合 USBPcap 可以抓取 USB 数据包,进行更深入的分析。
  • 兼容性: WebUSB 的兼容性还不是很好,需要根据目标用户的浏览器选择合适的方案。 一些旧版本的浏览器可能不支持 WebUSB。
  • 安全性: WebUSB 涉及硬件访问,需要注意安全性问题。 避免访问未知的 USB 设备,防止恶意代码攻击。 只信任来自安全来源的代码。
  • 异步编程: WebUSB 的 API 都是异步的,需要使用 async/awaitPromise 来处理异步操作。 理解异步编程是使用 WebUSB 的基础。

好了,这次是真的结束了!希望这些提示能帮到你。 祝大家在 WebUSB 的世界里玩的愉快,做出各种有趣的项目!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注