You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

USB串口长时间连接后触发DOMException缓冲区溢出问题排查

问题原因与解决方案

核心原因

  1. 缓冲区溢出:微控制器持续输出数据时,即使未与应用连接,串口内部缓冲区会不断积累数据。等待一段时间后再连接,缓冲区已被填满,读取时触发溢出错误。
  2. 流锁定冲突:原代码中while (port.readable)循环会重复尝试将port.readable管道到新的TextDecoderStream。可读流一旦被管道到可写流就会被锁定,若前一次管道未完全关闭就再次尝试,会触发"无法管道已锁定流"的错误。

修复方案

1. 清空累积的缓冲区

打开串口后,先读取并丢弃缓冲区中已积累的旧数据,确保从最新数据开始读取,避免溢出。

2. 正确管理流管道

移除外层while (port.readable)循环,改用递归的读取函数,仅在前一次管道完全关闭后才重启读取流程,确保流锁定被正确释放。

修改后的完整代码

import React, { useState } from 'react'

class LineBreakTransformer {
  constructor() {
    this.chunks = ''
  }

  transform(chunk, controller) {
    this.chunks += chunk
    const lines = this.chunks.split('\r\n')
    this.chunks = lines.pop()
    lines.forEach((line) => controller.enqueue(line))
  }

  flush(controller) {
    controller.enqueue(this.chunks)
  }
}

const App = () => {
  const [macAddr, setMacAddr] = useState('')
  const [sensors, setSensors] = useState([])

  // 清空串口缓冲区
  async function flushPort(port) {
    if (!port.readable) return
    const reader = port.readable.getReader()
    try {
      while (true) {
        const { done } = await reader.read()
        if (done) break
      }
    } catch (e) {
      // 忽略清空过程中的错误
    } finally {
      reader.releaseLock()
    }
  }

  async function onButtonClick() {
    const port = await navigator.serial.requestPort()
    await port.open({ baudRate: 115200, bufferSize: 10000000 })
    
    // 打开串口后立即清空缓冲区
    await flushPort(port)

    // 递归读取循环,确保流管道正确释放
    async function readLoop() {
      if (!port.readable) return
      
      const textDecoder = new TextDecoderStream()
      const readableStreamClosed = port.readable.pipeTo(textDecoder.writable)
      const reader = textDecoder.readable
        .pipeThrough(new TransformStream(new LineBreakTransformer()))
        .getReader()

      try {
        while (true) {
          const { value, done } = await reader.read()
          if (done) {
            break
          }
          if (value) {
            try {
              const { addr, ...sensors } = JSON.parse(value)
              setMacAddr(addr)
              setSensors(sensors)
            } catch (parseError) {
              console.error('JSON解析失败:', parseError)
            }
          }
        }
      } catch (error) {
        console.error('读取错误:', error)
      } finally {
        reader.releaseLock()
        // 等待流完全关闭后再尝试重启
        await readableStreamClosed.catch(() => {})
        // 若串口仍可读,重启读取循环
        if (port.readable) {
          readLoop()
        }
      }
    }

    // 启动初始读取循环
    readLoop()
  }

  return (
    <div>
      <h1>{`macAddr: ${macAddr}`}</h1>
      {Object.keys(sensors).map((sensor) => (
        <div key={sensor}>
          <div>{`Channel: ${sensor}`}</div>
          <div>{`Temp: ${sensors[sensor].value}`}</div>
        </div>
      ))}
      <button onClick={onButtonClick}>
        连接设备
      </button>
    </div>
  )
}

export default App

关键修改点说明

  • flushPort函数:打开串口后立即清空缓冲区,丢弃所有连接前积累的数据,避免溢出。
  • 递归readLoop:替代原外层循环,确保每次读取流程完全结束(流关闭、锁释放)后才重启,解决流锁定冲突。
  • 错误处理增强:增加JSON解析错误捕获,避免单个无效数据导致整个读取流程中断。

内容的提问来源于stack exchange,提问作者sshakya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 08:20:25