USB串口长时间连接后触发DOMException缓冲区溢出问题排查
问题原因与解决方案
核心原因
- 缓冲区溢出:微控制器持续输出数据时,即使未与应用连接,串口内部缓冲区会不断积累数据。等待一段时间后再连接,缓冲区已被填满,读取时触发溢出错误。
- 流锁定冲突:原代码中
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
相关产品推荐
相关产品推荐

