建议:在 ExecuteRequestAsync 中增加自动重连机制
问题描述
ModbusTcpClient 在长期运行的数采任务中,一旦 TCP 连接因网络中断(如网线松动、设备重启)断开,无法自动恢复通信。
分析调用链:
TcpModbusDevice.StartLoopAsync
└─ Client.ConnectAsync(cancelToken) // 只在循环开始前连接一次
└─ while (true)
└─ Client.ReadHoldingRegisters(...)
└─ ModbusClientBase.ExecuteRequestAsync
├─ if (!IsConnected)
│ └─ throw ModbusConnectionException("客户端未连接")
└─ Transport.SendReceiveAsync(...)
└─ if (!IsConnected)
└─ throw ModbusConnectionException("TCP 连接未建立")
连接断开后的演变:
TcpTransport.SendReceiveAsync 检测到 !IsConnected,抛 "TCP 连接未建立"
ModbusClientBase.ExecuteRequestAsync 捕获后重试(受 Retries 控制),重试耗尽后抛给上层
- 上层
catch (Exception ex) 仅记录日志,不触发重连
- 从此每次轮询都秒抛异常,网线恢复后也无法恢复通信
涉及文件
ModbusClientBase.cs - ExecuteRequestAsync
TcpTransport.cs - SendReceiveAsync, IsConnected
改进建议
建议1:ExecuteRequestAsync 中自动重连(核心)
protected async Task<ModbusResponse> ExecuteRequestAsync(ModbusRequest request, CancellationToken cancelToken)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsConnected)
{
await ConnectAsync(cancelToken); // 自动重连一次
}
// ... 后续逻辑
}
通过可配置属性控制,保持向后兼容:
public bool AutoReconnect { get; set; } = false;
建议2:区分"未连接"异常类型
目前 ModbusConnectionException 在以下场景被抛出,但异常消息是字符串,上层难以做针对性处理:
| 场景 |
异常消息 |
改进建议 |
ConnectAsync 连接失败 |
"TCP 连接失败: ..." |
维持现状 |
ExecuteRequestAsync 检测到未连接 |
"客户端未连接" |
建议添加连接状态枚举属性或定义子类 |
SendReceiveAsync 检测到未连接 |
"TCP 连接未建立" |
同上 |
建议:
public enum ConnectionState
{
NeverConnected, // 从未连接过
Disconnected, // 曾连接但已断开
Connected // 已连接
}
public class ModbusConnectionException : Exception
{
public ConnectionState ConnectionState { get; }
}
建议3:配置 KeepAlive 探测间隔
TcpTransport.ConnectAsync 中已启用 KeepAlive:
_tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
但 Windows 默认 keepalive 探测间隔为 2 小时(7200秒),断线检测极其滞后。建议增加可配置的 KeepAlive 参数:
public class NetworkConfig
{
// 新增选项
public bool KeepAlive { get; set; } = true;
public int KeepAliveInterval { get; set; } = 10; // 探测间隔(秒)
public int KeepAliveRetries { get; set; } = 5; // 重试次数
}
对应实现(Windows IOControl):
if (config.KeepAlive && _tcpClient.Client != null)
{
byte[] keepAlive = new byte[12];
Buffer.BlockCopy(BitConverter.GetBytes(1), 0, keepAlive, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(config.KeepAliveInterval * 1000), 0, keepAlive, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(config.KeepAliveInterval * 1000), 0, keepAlive, 8, 4);
_tcpClient.Client.IOControl(IOControlCode.KeepAliveValues, keepAlive, null);
}
影响范围
ModbusClientBase(所有客户端类型的基类)
TcpTransport(TCP 传输层)
- 所有使用
IModbusClient 的上层调用方
版本信息
建议:在
ExecuteRequestAsync中增加自动重连机制问题描述
ModbusTcpClient在长期运行的数采任务中,一旦 TCP 连接因网络中断(如网线松动、设备重启)断开,无法自动恢复通信。分析调用链:
连接断开后的演变:
TcpTransport.SendReceiveAsync检测到!IsConnected,抛"TCP 连接未建立"ModbusClientBase.ExecuteRequestAsync捕获后重试(受Retries控制),重试耗尽后抛给上层catch (Exception ex)仅记录日志,不触发重连涉及文件
ModbusClientBase.cs- ExecuteRequestAsyncTcpTransport.cs- SendReceiveAsync, IsConnected改进建议
建议1:ExecuteRequestAsync 中自动重连(核心)
通过可配置属性控制,保持向后兼容:
建议2:区分"未连接"异常类型
目前
ModbusConnectionException在以下场景被抛出,但异常消息是字符串,上层难以做针对性处理:ConnectAsync连接失败"TCP 连接失败: ..."ExecuteRequestAsync检测到未连接"客户端未连接"SendReceiveAsync检测到未连接"TCP 连接未建立"建议:
建议3:配置 KeepAlive 探测间隔
TcpTransport.ConnectAsync中已启用 KeepAlive:但 Windows 默认 keepalive 探测间隔为 2 小时(7200秒),断线检测极其滞后。建议增加可配置的 KeepAlive 参数:
对应实现(Windows IOControl):
影响范围
ModbusClientBase(所有客户端类型的基类)TcpTransport(TCP 传输层)IModbusClient的上层调用方版本信息