TCP读取称重秤重量:连续称重求和实现及异常排查
问题分析与解决方案
异常直接原因
System.IndexOutOfRangeException的触发核心是读取到的响应不符合预期格式,导致Split(',')后的数组长度不足3,无法访问索引为2的元素。常见场景:
Read方法中netStream.DataAvailable为false时,直接返回空字符串,Trim()后仍为空,Split(',')得到仅含空元素的数组,访问索引2必然报错。- TCP是流式传输,单次
netStream.Read可能只读取到部分响应,拆分后元素数量不足。
现有实现的核心问题
1. Read方法可靠性不足
- 依赖
DataAvailable判断数据是否到达,网络延迟会导致指令发送后数据未及时到达,直接返回空字符串。 - 未处理TCP粘包/拆包问题,单次读取无法保证获取完整响应。
2. DoubleWeighing逻辑存在漏洞
- 无响应格式校验,直接拆分访问数组索引,异常场景下必然崩溃。
- 两次读取间隔未考虑称重秤的数值稳定时间,可能读取到未稳定的无效值。
- 时间差判断粗糙,未处理两次读取过于接近(秤数值未更新)的情况。
修复与优化方案
1. 重构Read方法,保证完整响应读取
修改Read方法,通过循环读取直到检测到响应结束符(假设响应以\r\n结尾,与指令格式匹配):
private static string command = "READ\r\n"; private static readonly string responseTerminator = "\r\n"; public static string Read(NetworkStream netStream) { // 发送指令 byte[] commandBuffer = Encoding.UTF8.GetBytes(command); netStream.Write(commandBuffer, 0, commandBuffer.Length); StringBuilder receivedString = new StringBuilder(); byte[] readingBuffer = new byte[256]; int bytesRead; // 循环读取直到获取完整响应 while (!receivedString.ToString().EndsWith(responseTerminator)) { if (netStream.DataAvailable) { bytesRead = netStream.Read(readingBuffer, 0, readingBuffer.Length); receivedString.Append(Encoding.ASCII.GetString(readingBuffer, 0, bytesRead)); } else { // 短暂等待避免CPU空转 Thread.Sleep(10); } } // 返回去除结尾换行的有效内容 return receivedString.ToString().TrimEnd('\r', '\n'); }
优化点:
- 用
StringBuilder替代字符串拼接,提升性能。 - 循环读取直到检测到结束符,确保获取完整响应。
- 空等待时加入短暂休眠,降低CPU占用。
2. 优化DoubleWeighing方法,增加格式校验
public static string DoubleWeighing(NetworkStream netStream) { // 读取第一次重量并校验格式 string firstResponse = Read(netStream); double firstWeight; if (!TryParseWeight(firstResponse, out firstWeight)) { return firstResponse; } DateTime firstTime = DateTime.Now; // 等待秤数值稳定(可根据实际设备调整时长) Thread.Sleep(1000); // 读取第二次重量并校验格式 string secondResponse = Read(netStream); double secondWeight; if (!TryParseWeight(secondResponse, out secondWeight)) { return firstResponse; } DateTime secondTime = DateTime.Now; TimeSpan interval = secondTime - firstTime; // 优化判断逻辑:考虑秤精度误差,设置合理间隔范围 if (Math.Abs(secondWeight - firstWeight) > 0.1 && secondWeight > 0 && interval.TotalSeconds >= 1 && interval.TotalSeconds <= 60) { double total = firstWeight + secondWeight; // 保持原格式的空格对齐 string[] parts = secondResponse.Split(','); parts[2] = total.ToString("F1").PadLeft(4); return string.Join(",", parts); } else { return firstResponse; } } // 辅助方法:统一处理重量解析与格式校验 private static bool TryParseWeight(string response, out double weight) { weight = 0; string[] parts = response.Split(','); // 预期格式为ST,NT, 0,kg,共4个元素 if (parts.Length != 4) { return false; } // 去除空格后尝试转换 return double.TryParse(parts[2].Trim(), out weight); }
优化点:
- 新增
TryParseWeight辅助方法,统一格式校验与重量转换,避免数组越界。 - 用精度阈值替代直接判等,适配称重秤的微小误差。
- 增加两次读取的等待时间,确保数值稳定。
- 替换重量值时保持原格式的空格对齐,输出格式统一。
3. Main方法调用示例
public static void Main(String[] args) { TcpClient tcpClient = Connect.ConnectionSetup(IPAddress.Parse(ipAddress), portNumber); NetworkStream netStream = tcpClient.GetStream(); while (true) { var timestamp = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss.fff"); var response = DoubleWeighing(netStream); Console.WriteLine($"{timestamp} - {response}"); Thread.Sleep(1000); } }
总结
原问题的核心是Read方法无法保证获取完整合法的响应,加上DoubleWeighing缺乏格式校验导致数组越界。通过修复Read方法的可靠性,增加格式校验与逻辑优化,可解决异常问题并提升称重逻辑的合理性。
内容的提问来源于stack exchange,提问作者Daniel Gattesco
相关产品推荐
相关产品推荐

