Wago PFC200控制器通过MQTT_Client库连接MQTT Broker时TCP连接超时故障排查咨询
看起来你已经做了不少基础排查,但还是卡在TCP连接这一步——既然Socket能创建成功,问题大概率出在地址解析、网络通路或者Socket配置上,结合你的代码和工业控制器的特性,给你几个具体的排查方向:
1. 先修复地址解析的逻辑漏洞
你代码里判断Broker地址是IP还是域名的方式太粗糙了:只检查最后一位是不是数字,万一遇到mqtt1.example.com这种域名,会被错误当成IP去调用SysSockInetAddr,这个函数解析域名会返回0地址,直接导致连接失败。
换个更可靠的解析逻辑:先尝试用SysSockInetAddr解析(针对IP地址),失败了再用SysSockGetHostByName做域名解析,代码改成这样:
sockAddr.sin_family := SOCKET_AF_INET; // 先尝试解析为IP地址 sockAddr.sin_addr := SysSockInetAddr(i_sBrokerAddress); // 如果解析失败(返回0),再尝试域名解析 IF sockAddr.sin_addr = 0 THEN sockAddr.sin_addr := SysSockGetHostByName(i_sBrokerAddress); // 这里可以加个判断,如果域名解析也失败,直接抛出地址错误 IF sockAddr.sin_addr = 0 THEN q_sDiagMsg := 'Failed to resolve broker address'; q_xError := TRUE; RETURN; END_IF END_IF sockAddr.sin_port := SysSockHtons(i_uiPort);
这样不管输入是IP还是域名,都能正确处理,避免误判。
2. 排查PFC200的网络通路与防火墙
工业控制器的网络限制比普通PC多得多,这是最容易忽略的点:
- 登录PFC200的Web管理界面,检查防火墙规则,确保允许出站到MQTT端口(默认1883,加密用8883)的TCP连接
- 确认控制器所在的工业网络没有ACL限制:比如交换机是否禁止了控制器访问外部Broker的端口,网关是否有出站过滤
- 如果能访问控制器的命令行,直接用工具测试连通性:
# 测试Broker是否可达 ping <你的Broker IP/域名> # 测试端口是否开放 telnet <你的Broker IP/域名> 1883
如果telnet连不上,那肯定是网络层面的问题,不用再纠结代码了。
3. 拿到SysSockConnect的具体错误码
你现在只判断了xResult的真假,但SysSockConnect失败时会有具体的错误码,用SysSockGetLastError()就能拿到,把它加到诊断信息里,能直接定位问题:
xResult := SysSockConnect(diSocket, ADR(sockAddr), SIZEOF(sockAddr)); q_sDiagMsg:='Connect to TCP-Server'; IF (xResult) THEN q_udiState:=15; ELSE // 新增错误码输出 q_sDiagMsg := 'Connect failed, error code: ' + INT_TO_STRING(SysSockGetLastError()); END_IF
常见错误码对应的问题:
WSAETIMEDOUT:网络不通,确实超时WSAECONNREFUSED:Broker没运行,或者端口没开放WSAEHOSTUNREACH:主机不可达,地址解析错了或者路由有问题WSAEINVAL:Socket状态不对,或者地址结构有误
4. 检查Socket超时的配置逻辑
你设置了wTimeOutValue:=1000,但要确认这个值有没有正确关联到TON_TimeOut的PT参数,而且SysSockConnect本身是阻塞调用,建议在调用前启动TON,同时给Socket设置内置超时(避免系统默认超时和你的TON不同步):
// 在创建Socket后,设置发送/接收超时 VAR stTimeout : TIMEVAL; END_VAR stTimeout.tv_sec := 10; // 10秒超时,根据需要调整 stTimeout.tv_usec := 0; SysSockSetsockopt(diSocket, SOL_SOCKET, SO_SNDTIMEO, ADR(stTimeout), SIZEOF(stTimeout)); SysSockSetsockopt(diSocket, SOL_SOCKET, SO_RCVTIMEO, ADR(stTimeout), SIZEOF(stTimeout)); // 启动超时定时器 TON_TimeOut(IN := TRUE, PT := wTimeOutValue); // 调用连接 xResult := SysSockConnect(diSocket, ADR(sockAddr), SIZEOF(sockAddr)); // 停止定时器 TON_TimeOut(IN := FALSE);
这样能确保超时逻辑的一致性,不会出现Socket还在等,你的TON已经触发的情况。
5. 确认库与固件的兼容性
最后检查一下版本匹配:
- 你用的Stefan Rossmann的MQTT_Client库是不是最新版本?旧版本可能和新的e!cockpit或者PFC200固件不兼容
- 确认PFC200的固件是官方最新稳定版,有时候固件的Socket API会有更新,旧库可能适配不好
先从这几个方向排查,应该能找到问题所在。
内容的提问来源于stack exchange,提问作者sashilio




