news 2026/8/28 22:05:34

C# 工业设备通讯系列(持续更新中...)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# 工业设备通讯系列(持续更新中...)

文章目录

    • C# 工业设备通讯系列技术博文(持续更新...)
    • 第一篇:【C# 工业通讯】封装一个健壮的 TCP Socket 客户端基类(心跳与异常处理)
  • 核心痛点
    • 解决方案:通用客户端基类设计
    • 关键技术点:
    • 核心代码实现
    • 设计要点总结
    • 总结

C# 工业设备通讯系列技术博文(持续更新…)

系列说明: 本系列基于实际工业机械臂控制项目,分享 C# 底层 Socket 通讯封装、设计模式应用、高频数据异步读取以及单通道通讯设计的实战经验。

第一篇:【C# 工业通讯】封装一个健壮的 TCP Socket 客户端基类(心跳与异常处理)

在工业自动化开发中,与机械臂、PLC 等设备的 TCP 通讯是基础。本文分享一个基于 C# Socket 封装的通用客户端基类,重点解决了连接状态探测(Poll)、KeepAlive 心跳保活以及异常断开的统一处理。

核心痛点

在使用原生 Socket 开发工业设备通讯时,我们经常遇到以下问题:
•假连接: socket.Connected 属性并不总是准确的,网线被拔了可能还是返回 true。
心跳保活: 长时间无数据传输,路由器或防火墙可能会切断连接,导致对端认为你已离线。
代码复用: 每个设备都写一套连接、断开、发送逻辑,维护成本高,且容易出现遗漏。

解决方案:通用客户端基类设计

我设计了一个抽象类作为底层通讯基类,将通用的连接管理逻辑抽离出来,子类只需关注具体的业务协议。

关键技术点:

精准的状态检测: 使用 Poll 方法配合 SelectMode.SelectRead 来探测连接是否真实有效。
底层 KeepAlive: 通过 IOControl 设置 TCP 层的保活机制,防止连接被静默断开。
事件驱动: 定义 NetworkErrorEvent,将网络异常统一抛给上层处理。

核心代码实现

usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Runtime.InteropServices;usingSystem.Text;namespaceJupiter.Device{/// <summary>/// 设备客户端基类 - 封装底层 Socket 通讯细节/// 所有机械臂/PLC等设备的通讯类均应继承此类/// </summary>publicabstractclassDeviceClient{privateSocketmSocketClient=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);publicstringIP{get;privateset;}publicintPort{get;privateset;}/// <summary>/// 连接设备(入口方法,处理重复连接和地址变更)/// </summary>publicboolConnect(stringstrIp,intiPort){// 如果已连接且地址未变,直接返回,避免重复建连if(null!=mSocketClient&&IsConnected()){if(strIp!=this.IP||iPort!=this.Port){this.Disconnect();}else{returntrue;}}returnthis.ConnectDeviceServer(strIp,iPort);}/// <summary>/// 真实连接状态检测/// 解决 Connected 属性不准的问题:网线拔了可能还是 true/// </summary>publicboolIsConnected(){try{if(mSocketClient==null||!mSocketClient.Connected)returnfalse;// 核心:使用 Poll 探测连接状态// 如果 SelectRead 为 true 且 Available 为 0,说明连接已被对端关闭boolstateChanged=mSocketClient.Poll(1000,SelectMode.SelectRead);if(stateChanged&&mSocketClient.Available==0){returnfalse;}returntrue;}catch(Exceptionex){LogHelper.Error("DeviceClient",ex,"连接检测异常");returnfalse;}}/// <summary>/// 建立物理连接并开启 KeepAlive/// </summary>privateboolConnectDeviceServer(stringstrIp,intiPort){try{this.IP=strIp;this.Port=iPort;mSocketClient=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);mSocketClient.Connect(newIPEndPoint(IPAddress.Parse(strIp),iPort));// 【重点】开启 TCP KeepAlive 防止连接被静默断开// 参数说明:启用(1) + 首次探测时间(5000ms) + 探测间隔(500ms)uintdummy=0;byte[]optVal=newbyte[Marshal.SizeOf(dummy)*3];BitConverter.GetBytes((uint)1).CopyTo(optVal,0);BitConverter.GetBytes((uint)5000).CopyTo(optVal,Marshal.SizeOf(dummy));BitConverter.GetBytes((uint)500).CopyTo(optVal,Marshal.SizeOf(dummy)*2);mSocketClient.IOControl(IOControlCode.KeepAliveValues,optVal,null);OnConnected(mSocketClient);returntrue;}catch(Exceptionex){LogHelper.Error("DeviceClient",ex,"连接失败");returnfalse;}}/// <summary>/// 发送数据(UTF-8 文本协议)/// </summary>protectedboolSendData(stringstr){try{byte[]data=Encoding.UTF8.GetBytes(str);return(mSocketClient.Send(data)==data.Length);}catch(SocketExceptionex){// 发送失败且连接断开,触发网络错误事件if(!IsConnected())NetworkErrorEvent?.Invoke(this,ex.SocketErrorCode);returnfalse;}}/// <summary>/// 断开连接/// </summary>publicvoidDisconnect(){try{if(mSocketClient!=null&&mSocketClient.Connected){mSocketClient.Shutdown(SocketShutdown.Both);mSocketClient.Close();}}catch(Exceptionex){LogHelper.Error("DeviceClient",ex,"断开连接异常");}finally{OnDisconnected();}}/// <summary>/// 网络异常事件,供上层订阅/// </summary>publicdelegatevoidOnNetworkError(DeviceClientsender,SocketErroriErrCode);publiceventOnNetworkErrorNetworkErrorEvent;// 抽象方法,强制子类实现连接成功/断开后的业务逻辑protectedabstractvoidOnConnected(Socketsock);protectedabstractvoidOnDisconnected();}}

设计要点总结

设计点说明
IsConnected() 重写用 Poll + Available 替代简单的 Connected 属性判断,能准确检测网线拔出等物理断开
TCP KeepAlive通过 IOControl 设置底层保活参数,首次探测 5s 后开始,间隔 500ms,有效防止路由器/防火墙切断空闲连接
抽象方法隔离OnConnected / OnDisconnected 交由子类实现,基类只负责连接生命周期管理
事件驱动异常NetworkErrorEvent 让上层决定如何处理异常(重试、告警、降级等),基类不耦合具体策略

总结

通过封装这个通用客户端基类,我们将繁琐的 Socket 状态管理、字节流转换和异常捕获下沉到了基类。后续开发具体的机械臂控制类时,只需要继承它并实现 OnConnected 即可,大大提升了代码的可维护性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/28 22:02:30

MATLAB预测模型实战:从灰色预测到神经网络,掌握四大核心算法

1. 从“猜”到“算”&#xff1a;预测模型到底在做什么&#xff1f; 每次看到“预测”两个字&#xff0c;很多人第一反应是“算命”或者“瞎猜”。尤其是在数学建模竞赛里&#xff0c;新手拿到一个预测类题目&#xff0c;比如“预测未来五年的城市用电量”、“估计下个月某商品…

作者头像 李华
网站建设 2026/8/28 22:02:28

C++11 std::function与std::bind:可调用对象封装与适配实战

1. 从“函数对象”到“可调用对象”&#xff1a;C11的范式跃迁如果你是从C98/03时代一路走过来的老手&#xff0c;肯定对“仿函数”&#xff08;Functor&#xff09;这个概念不陌生。那时候&#xff0c;想让一个东西像函数一样被调用&#xff0c;最正统的做法就是定义一个类&am…

作者头像 李华
网站建设 2026/8/28 22:00:26

蓝桥杯算法题解析:状态压缩DP在网格计数问题中的应用

1. 从一道蓝桥杯算法题看“绘制地图”的抽象与实现 最近在整理蓝桥杯的历年练习题&#xff0c;翻到了ALGO-380这道名为“绘制地图”的题目。说实话&#xff0c;第一次看到这个标题&#xff0c;我脑海里浮现的是各种图形库、画布操作&#xff0c;甚至想到了游戏开发里的地图编辑…

作者头像 李华
网站建设 2026/8/28 21:56:36

Hadoop Reduce OOM 真实排查案例(Hive On MapReduce)

本质&#xff1a;脏数据 → 数据倾斜 → OOM业务背景&#xff1a;用户行为日志每日离线跑批&#xff0c;Hive 执行 group by user_id 统计用户行为次数。每日跑批&#xff0c;某天作业突然失败&#xff0c;Reduce 任务 OOM&#xff0c;绝大多数 task 很快跑完&#xff0c;仅 1 …

作者头像 李华
网站建设 2026/8/28 21:54:34

视频字幕怎么提取?2026年5种实用方法,没字幕也能自动转文字

看视频最烦的就是&#xff1a;明明只想快速知道讲了什么&#xff0c;却得把十几二十分钟完整看完。尤其是做内容的人&#xff0c;拆同行视频、整理选题素材&#xff0c;经常需要先把字幕或文案弄出来扫一眼。 这事现在已经挺好解决了&#xff1a;视频本身有字幕&#xff0c;可以…

作者头像 李华
网站建设 2026/8/28 21:53:06

已有微信小程序如何迁移到 FinClip 中运行,并完成测试与上架

已有微信小程序&#xff0c;想在自有 APP 里继续使用&#xff0c;通常不需要把所有页面重新做成原生页面。迁移的重点不在于“把代码包上传一次”&#xff0c;而在于把原来依赖微信环境的能力梳理清楚&#xff0c;再接入宿主 APP 和小程序管理平台。 FinClip 在其中承担两部分工…

作者头像 李华