You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:57:59