返回知识中心
平台

Web Serial API 端口权限与生命周期

通过用户操作请求 Web Serial,区分权限与端口就绪状态,限制数据流,处理断开并保留安全备用路径。

BotBrowser Team

文档中心

想直接进入 平台 文档吗?

这篇文章属于知识中心的指南库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

Web Serial 允许页面请求用户选择的串行端口,但选择器结果不等于已连接、可信设备,也不能证明设备归谁所有。BotBrowser 可以在受控 context 中验证页面自有的权限和备用分支,但不会制造实体串行设备、绕过浏览器授予操作系统权限,也不能证明硬件 I/O。应明确约束:由用户操作发起,只打开支持的端口,限制每条数据帧,断开时取消并关闭 streams,并让任务在没有串行硬件时仍可用。

用户操作、Web Serial 选择器权限、SerialPort 打开状态、有限 streams、断开清理和备用路径

TL;DR

  • navigator.serial.requestPort({ filters: [{ usbVendorId: 0x2341 }] }) 需要安全上下文和短暂的用户激活。
  • 选择的 SerialPort 是来源范围内的能力,不证明端口已打开、连接、安全或协议兼容。
  • 使用审查后的 port.open({ baudRate: 115200 }),并在改变状态前校验每条解码帧。
  • connect 和 disconnect 是浏览器观察到的变化;取消 readers、释放 locks、关闭端口并保留草稿。
  • 保存 granted、unsupported、invalid-frame 或 disconnected,不保存序列号、原始缓冲区或推断身份。

目录

在使用时请求

Web Serial 仅限安全上下文,requestPort() 应在用户操作期间运行。检测 navigator.serial 只代表方法存在,不会打开选择器;iframe 策略、受管浏览器、平台或操作系统都可能拒绝请求。

async function connectSerial() {
  if (!window.isSecureContext || !navigator.serial) return showSerialState('unsupported');
  setSerialState('requesting');
  try {
    const port = await navigator.serial.requestPort({
      filters: [{ usbVendorId: 0x2341 }],
    });
    await openSelectedPort(port);
  } catch (error) {
    showSerialState(error.name === 'NotFoundError' ? 'cancelled' : 'denied');
  }
}

connectButton.addEventListener('click', connectSerial);

不要在加载时打开选择器,也不要在取消后循环请求。保留草稿,等待新的明确操作。WebUSB 设备访问:权限、接口与隐私区分授权和会话就绪;WebHID 设备权限与隐私专门讨论 HID 报告生命周期;浏览器权限指纹说明权限状态不等于授权或身份。

区分权限与端口就绪

requestPort() 返回浏览器选择的端口,但它可能已断开、使用错误 baudRate 或运行不同协议。把选择、打开、读取、写入和关闭视为不同状态,并记录支持的 baudRate、dataBits、stopBits、parity 与 flowControl。配置错误可能产生看似合理但无效的字节,因此要在 decoder 后校验协议。

navigator.serial.getPorts() 不是完整硬件清单。重新连接应由用户操作触发并重新检查配置。Web Serial 规范与 MDN Web Serial API描述接口,但不证明身份、地点或实体结果。

限制可读和可写 streams

port.readable 与 port.writable 是异步 streams。使用最大长度、分隔符或长度字段;协议有 checksum 时校验,并为不完整帧设置超时。

const MAX_FRAME_BYTES = 128;

async function readSerialFrames(port) {
  const reader = port.readable.getReader();
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      if (value.byteLength > MAX_FRAME_BYTES) {
        showSerialState('invalid-frame');
        continue;
      }
      acceptBoundedFrame(value);
    }
  } finally {
    reader.releaseLock();
  }
}

不要记录每个字节,也不要把任意帧交给命令解释器。将错误分类为 invalid-length、invalid-checksum 或 timeout。writer.write() 成功只证明浏览器接受了字节,不证明硬件产生效果;关闭前先释放 writer 并取消 reader。

const serialOutcome = { state: 'fallback', rawIdentifiersRetained: false };
showSerialState(serialOutcome.state);

协调 connect 与 disconnect

connect 表示浏览器观察到端口出现,disconnect 表示观察到端口移除;两者都不说明原因,也不允许静默请求。断开时先让任务终止,再取消 reader、释放 writer、尽力执行 port.close()、移除 listeners、保留数据并显示重连按钮。不要自动重连或重发上一条命令。

使用 idle、requesting、selected、opening、opened、reading、invalid-frame、disconnected、cancelled 和 fallback 状态。端口回来后重新执行配置与 handshake,不要把 serialNumber 或 getInfo() 当成身份。

最小化元数据并保留备用路径

把浏览器权限、串行会话、协议同意和远程上传分开。本地读取不授权服务器上传。运行记录通常只需 unsupported、denied、cancelled、opened、invalid-frame、disconnected 或 fallback、时间和测试环境;避免 getInfo()、USB 标识符、原始缓冲区和长期历史。保留手动输入、文件导入、模拟值或静态配置。

用决策表分类结果

观察阶段能证明什么安全下一步
API 缺失、不安全上下文或策略阻止当前无法启动 Web Serial保留无串行任务并说明限制
选择器取消或权限拒绝本次没有可用授权保留草稿,等待明确操作
端口已选但 open() 拒绝没有可用会话显示配置、关闭并不扩大访问
reader 报告无效帧输入未通过协议丢弃、分类并继续任务
disconnect 事件浏览器观察到端口移除取消 streams、关闭、保留数据并让用户重试
有效且受限的帧页面接受一个值只报告该值,不推断实体效果

BotBrowser 的能力与限制

BotBrowser 受控 context 可以验证连接按钮、能力检测、选择器结果、帧校验、断开清理和无串行备用路径。高级能力文档描述了可检查的浏览器能力。它不提供实体串行外设、不授予系统权限、不保证端口,也不证明字节改变了硬件。BotBrowser 可以验证 navigator.serial 的可见状态和备用路径,但不会验证真实设备的电气操作。

{
  "api": "navigator.serial",
  "permission": "denied",
  "portState": "fallback",
  "framesAccepted": 0,
  "rawIdentifiersRetained": false,
  "physicalDevice": "not-asserted"
}

改变浏览器、策略、权限、配置、模拟帧或断开条件后重跑 fixture。比较第一个可见状态差异,不要承诺通用硬件支持。

来源

实践结论

使用明确操作,区分权限、打开和 streams,校验每条帧,并在断开后关闭前取消 readers。让任务在没有串行硬件时仍完整可用,记录失败阶段而不是端口元数据。浏览器、策略、驱动、固件或权限变化后重跑 fixture;页面成功不代表实体硬件行为。

#Web Serial API#权限#串行端口#设备隐私#BotBrowser

本文属于指南: Web 平台能力面:一致性复核地图

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。