如何用UDP协议实现C#与以太网设备的帧通信?
C# UDP实现设备帧交互完整方案
没问题,我来帮你把现有的帧构造逻辑和UDP通信整合起来,实现和设备的完整数据交互。我们会用C#内置的UdpClient类来处理UDP的发送与接收,结合你提供的帧结构方法,完成端到端的通信流程。
一、基础UDP通信封装
首先我们创建一个专门的类来封装UDP通信逻辑,这样代码更清晰,也方便后续维护:
using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; public class DeviceUdpCommunicator { private readonly UdpClient _udpClient; private readonly IPEndPoint _deviceEndPoint; private int _sequenceNumber; // 帧序列号,每次发送后自增 // 构造函数:传入设备的IP和端口 public DeviceUdpCommunicator(string deviceIp, int devicePort) { _udpClient = new UdpClient(); _deviceEndPoint = new IPEndPoint(IPAddress.Parse(deviceIp), devicePort); _sequenceNumber = 0; // 初始序列号设为0,可根据设备要求调整 } // 你的现有帧构造方法,修正了方法名拼写错误(Fetc_SendHeaderInfo → Fetch_SendHeaderInfo) private byte[] Fetch_FrameID(bool bHostToDevice) //true:主机到设备 false:设备到主机 { return bHostToDevice ? new byte[] { 0x8F, 0xE1 } : new byte[] { 0x2D, 0x7A }; } private byte[] Fetch_FrameSequenceNumber(int iNumber) { return new byte[] { (byte)(iNumber & 0xFF), (byte)((iNumber >> 8) & 0xFF) }; } private byte[] Fetch_HostToDeviceCommand(int iStatus) //status:0:激活 1:同步 2:请求 { switch (iStatus) { case 0: return new byte[] { 0x00, 0x00 }; //激活 case 1: return new byte[] { 0x11, 0x00 }; //同步 case 2: return new byte[] { 0x21, 0x00 }; //请求 default: return null; } } private byte[] Fetch_DataLen(byte[] dataArray) { int iLength = dataArray == null ? 0 : dataArray.Length; return new byte[] { (byte)(iLength & 0xFF), (byte)((iLength >> 8) & 0xFF) }; } private byte[] Fetch_DataCheckSum(byte[] dataArray) { int iSum = 0; if (dataArray != null) { for (int i = 0; i < dataArray.Length; i++) { iSum += (int)dataArray[i]; } } iSum += Convert.ToInt32("0xAA55", 16); return new byte[] { (byte)(iSum & 0xFF), (byte)((iSum >> 8) & 0xFF) }; } private byte[] Fetch_SendHeaderInfo(int iStatus, int iNumber, byte[] dataArray) //status:0:激活 1:同步 2:请求 { List<byte> result = new List<byte>();//共12字节,每项2字节 result.AddRange(Fetch_FrameID(true));//获取帧标识 result.AddRange(Fetch_FrameSequenceNumber(iNumber));//获取帧序列号 result.AddRange(Fetch_HostToDeviceCommand(iStatus));//获取主机到设备指令 result.AddRange(Fetch_DataLen(dataArray));//获取数据长度 result.AddRange(Fetch_DataCheckSum(dataArray)); //获取数据校验和 result.AddRange(Fetch_DataCheckSum(result.ToArray())); //获取头部数据校验和 return result.ToArray(); } }
二、实现发送帧方法
接下来在类中添加发送方法,把头部和数据(如果有)组合成完整的发送帧,然后通过UDP发送:
// 发送指定指令的帧,可附带数据 public void SendFrame(int commandStatus, byte[] data = null) { // 构造帧头部 byte[] header = Fetch_SendHeaderInfo(commandStatus, _sequenceNumber, data); // 组合头部和数据,形成完整帧 List<byte> fullFrame = new List<byte>(header); if (data != null) { fullFrame.AddRange(data); } // 发送数据到设备 _udpClient.Send(fullFrame.ToArray(), fullFrame.Count, _deviceEndPoint); // 序列号自增,注意不要超过设备支持的范围(这里假设是16位,即0~65535) _sequenceNumber = (_sequenceNumber + 1) % 65536; }
三、实现接收设备响应的方法
添加接收方法,用来监听设备返回的帧,并且可以验证帧的合法性(比如检查帧标识):
// 同步接收设备响应,超时时间可调整 public byte[] ReceiveResponse(int timeoutMs = 5000) { _udpClient.Client.ReceiveTimeout = timeoutMs; try { // 接收数据 byte[] receivedData = _udpClient.Receive(ref _deviceEndPoint); // 验证帧标识是否为设备到主机的标识 byte[] expectedFrameId = Fetch_FrameID(false); if (receivedData.Length >= 2 && receivedData[0] == expectedFrameId[0] && receivedData[1] == expectedFrameId[1]) { return receivedData; } else { throw new InvalidOperationException("接收到无效的设备响应帧标识"); } } catch (SocketException ex) { throw new TimeoutException("接收设备响应超时", ex); } }
四、使用示例
现在你可以这样调用这个类来和设备交互:
class Program { static void Main(string[] args) { // 替换成你的设备IP和端口 string deviceIp = "192.168.1.100"; int devicePort = 5000; using (var communicator = new DeviceUdpCommunicator(deviceIp, devicePort)) { try { // 发送激活指令 communicator.SendFrame(0); Console.WriteLine("已发送激活指令"); // 接收激活响应 byte[] activateResponse = communicator.ReceiveResponse(); Console.WriteLine($"收到激活响应,长度:{activateResponse.Length}字节"); // 发送请求指令,可附带数据(示例中无数据) communicator.SendFrame(2); byte[] requestResponse = communicator.ReceiveResponse(); Console.WriteLine($"收到请求响应,长度:{requestResponse.Length}字节"); // 解析响应数据(根据设备协议自行实现解析逻辑) ParseResponse(requestResponse); } catch (Exception ex) { Console.WriteLine($"通信出错:{ex.Message}"); } } } // 示例:解析设备响应(需根据设备协议调整) private static void ParseResponse(byte[] responseData) { // 这里根据设备的帧结构来解析,比如获取序列号、数据内容等 if (responseData.Length >= 12) { int sequenceNumber = BitConverter.ToUInt16(responseData, 2); Console.WriteLine($"响应帧序列号:{sequenceNumber}"); } } }
关键注意事项
- 序列号管理:确保序列号按设备要求循环,避免超出范围(比如16位的话就是0~65535)。
- 校验和验证:接收响应时,建议也对设备返回的帧做校验和验证,确保数据完整性(可以参考你现有的
Fetch_DataCheckSum方法实现响应校验)。 - 异常处理:UDP是无连接协议,可能出现丢包,所以要做好超时和重试逻辑(示例中只做了基础超时处理,可根据需求添加重试)。
- 异步通信:如果需要非阻塞通信,可以使用
UdpClient的异步方法(比如SendAsync和ReceiveAsync),适合UI程序。
内容的提问来源于stack exchange,提问作者peter
相关产品推荐
相关产品推荐

