串口IOException能否可靠恢复?Windows服务串口通信异常咨询
先明确你的场景:你运行一个Windows服务,通过COM4端口每秒约15次、全天候检测硬件,设备响应以>CRLF结尾。现有代码在ReadAsync调用时偶发如下IOException:
System.IO.IOException: The I/O operation has been aborted because Of either a thread Exit Or an application request.
异常频率极低(144小时出现2次,约380万次循环1次)且与服务停止无关。针对你的两个问题,解答如下:
1. 发生异常的该次循环数据是否永久丢失?
是的,本次循环中已读取的部分响应数据会直接丢失,而未读取的剩余数据能否被后续读取则取决于串口驱动的处理逻辑:
- 如果异常发生时已经读取了部分响应(比如
ABC>CRLF中的AB),这部分数据已经被追加到sData,但因为异常抛出,当前读取流程中断,这部分sData会被丢弃(原代码未捕获异常的情况下会直接向上抛出,导致整个SendAsync失败)。 - 对于未读取的剩余响应数据,大部分情况下如果是临时系统资源调度导致的异常,数据会留在串口输入缓冲区中;但也存在小概率因为操作中止导致驱动清空缓冲区的情况,此时这部分数据会永久丢失。
2. 若将ReadAsync()调用包裹在Try/Catch块中,下一次循环能否获取等待的响应?
有可能,但你的当前修改方案存在隐患:
现有修改的问题
你在捕获异常后直接清空了sData,如果之前已经读取了部分响应数据,这会导致后续读取的剩余数据无法和之前的内容拼接成完整响应。比如已经读取了AB,异常后清空sData,后续读取到C>CRLF,此时sData为C>CRLF,虽然匹配结尾,但这是不完整的响应,会导致业务逻辑出错。
优化后的处理方式
捕获异常时不要清空sData,只需要重新初始化aData,然后继续执行读取循环:
Do Try iSize = Await Me.Port.BaseStream.ReadAsync(aData, 0, aData.Length) sData &= Encoding.ASCII.GetString(aData, 0, iSize) Catch ex As IOException Main.Logger.Warn("IOException was skipped. Continuing to read remaining data.") aData = New Byte(255) {} ' 只重置缓冲区,保留已读取的sData End Try Loop While Not sData.EndsWith($">{vbCrLf}")
这样修改后,如果响应数据还在串口缓冲区中,下一次ReadAsync会读取剩余内容,最终拼接成完整的响应返回。但如果响应数据已经被丢弃,本次读取循环会一直等待,直到超时(如果设置了超时)或者硬件发送下一次响应——这种情况下你需要考虑添加超时机制,避免无限等待,同时在超时后重新发送指令。
额外建议
针对这种偶发异常,你还可以做这些优化:
- 添加异步读取超时:使用
CancellationTokenSource为ReadAsync设置超时时间,比如300ms,超时后触发取消操作,避免无限等待。 - 重新发送指令:如果读取超时或者连续多次捕获IOException,可以重新发送当前指令,确保硬件能收到请求并返回响应。
- 排查硬件/驱动问题:这种低频率的I/O中止异常可能和串口驱动稳定性、系统电源管理有关,尝试更新串口驱动,或者将Windows服务的进程优先级设置为“高”,减少线程被系统中止的概率。
内容的提问来源于stack exchange,提问作者InteXX

