当前位置: 首页 > news >正文

以太网与工业以太网通信C#开发

以太网与工业以太网通信C#开发

目录

  1. 以太网基础
  2. 工业以太网概述
  3. C#网络编程基础
  4. TCP/IP通信实现
  5. UDP通信实现
  6. 工业以太网协议
  7. Modbus TCP实现
  8. EtherNet/IP实现
  9. 实战案例

以太网基础

什么是以太网

以太网(Ethernet)是一种计算机局域网技术,是当今现有局域网采用的最通用的通信协议标准。

核心特点:

  • 物理层和数据链路层:工作在OSI模型的第1层和第2层
  • CSMA/CD协议:载波侦听多路访问/冲突检测
  • MAC地址:48位物理地址,用于设备识别
  • 帧结构:数据封装在以太网帧中传输

以太网帧结构

+---------------+---------------+----------+---------+-----+
| 目的MAC(6字节) | 源MAC(6字节)  | 类型(2)  | 数据     | FCS |
+---------------+---------------+----------+---------+-----+

OSI七层模型与TCP/IP四层模型

OSI七层模型TCP/IP四层模型功能
应用层应用层HTTP, FTP, DNS
表示层数据格式化
会话层会话管理
传输层传输层TCP, UDP
网络层网络层IP, ICMP
数据链路层数据链路层以太网, WiFi
物理层电气信号

工业以太网概述

工业以太网的特点

工业以太网是将标准以太网技术应用于工业自动化领域,相比标准以太网具有以下特点:

  1. 实时性要求高

    • 确定性通信延迟
    • 支持硬实时和软实时通信
  2. 可靠性强

    • 抗电磁干扰能力
    • 冗余机制
    • 工业级温度范围(-40℃ ~ 85℃)
  3. 安全性

    • 工业级安全认证
    • 故障安全机制
  4. 兼容性

    • 向下兼容标准以太网
    • 支持多种工业协议

主流工业以太网协议

协议制定者特点应用领域
Modbus TCPSchneider简单、开放过程控制
EtherNet/IPODVACIP协议族离散制造
PROFINETSiemens三种通信类别自动化
EtherCATBeckhoff极高实时性运动控制
PowerlinkB&R开源、实时运动控制
CC-Link IECLPA高速、大容量工厂自动化

C#网络编程基础

System.Net命名空间

C#通过System.NetSystem.Net.Sockets命名空间提供网络编程支持。

主要类:

  • Socket: 底层套接字操作
  • TcpClient / TcpListener: TCP客户端/服务器
  • UdpClient: UDP通信
  • IPAddress: IP地址表示
  • IPEndPoint: 网络端点(IP+端口)

基础示例:获取本机IP

using System;
using System.Net;
using System.Net.Sockets;
using System.Linq;public class NetworkHelper
{/// <summary>/// 获取本机IPv4地址/// </summary>public static string GetLocalIPv4(){var host = Dns.GetHostEntry(Dns.GetHostName());var ipAddress = host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);return ipAddress?.ToString() ?? "127.0.0.1";}/// <summary>/// 获取所有网络接口信息/// </summary>public static void PrintNetworkInterfaces(){var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();foreach (var ni in interfaces){Console.WriteLine($"接口: {ni.Name}");Console.WriteLine($"状态: {ni.OperationalStatus}");Console.WriteLine($"速度: {ni.Speed / 1_000_000} Mbps");Console.WriteLine($"MAC: {ni.GetPhysicalAddress()}");Console.WriteLine();}}
}

TCP/IP通信实现

TCP服务器实现

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;public class TcpServer
{private TcpListener _listener;private bool _isRunning;private List<TcpClient> _clients = new List<TcpClient>();public event Action<string> OnMessageReceived;public event Action<string> OnClientConnected;public event Action<string> OnClientDisconnected;/// <summary>/// 启动TCP服务器/// </summary>public async Task StartAsync(string ipAddress, int port){_listener = new TcpListener(IPAddress.Parse(ipAddress), port);_listener.Start();_isRunning = true;Console.WriteLine($"TCP服务器启动: {ipAddress}:{port}");while (_isRunning){try{var client = await _listener.AcceptTcpClientAsync();_clients.Add(client);var clientEndPoint = client.Client.RemoteEndPoint.ToString();Console.WriteLine($"客户端连接: {clientEndPoint}");OnClientConnected?.Invoke(clientEndPoint);// 为每个客户端创建独立的处理任务_ = Task.Run(() => HandleClientAsync(client));}catch (Exception ex){Console.WriteLine($"接受连接错误: {ex.Message}");}}}/// <summary>/// 处理客户端通信/// </summary>private async Task HandleClientAsync(TcpClient client){var clientEndPoint = client.Client.RemoteEndPoint.ToString();var stream = client.GetStream();var buffer = new byte[4096];try{while (client.Connected && _isRunning){var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead == 0){// 客户端断开连接break;}var message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到 [{clientEndPoint}]: {message}");OnMessageReceived?.Invoke(message);// 回显数据var response = $"服务器收到: {message}";var responseBytes = Encoding.UTF8.GetBytes(response);await stream.WriteAsync(responseBytes, 0, responseBytes.Length);}}catch (Exception ex){Console.WriteLine($"客户端通信错误 [{clientEndPoint}]: {ex.Message}");}finally{Console.WriteLine($"客户端断开: {clientEndPoint}");OnClientDisconnected?.Invoke(clientEndPoint);_clients.Remove(client);client.Close();}}/// <summary>/// 广播消息给所有客户端/// </summary>public async Task BroadcastAsync(string message){var data = Encoding.UTF8.GetBytes(message);var disconnectedClients = new List<TcpClient>();foreach (var client in _clients.ToArray()){try{if (client.Connected){var stream = client.GetStream();await stream.WriteAsync(data, 0, data.Length);}else{disconnectedClients.Add(client);}}catch{disconnectedClients.Add(client);}}// 清理断开的客户端foreach (var client in disconnectedClients){_clients.Remove(client);client.Close();}}/// <summary>/// 停止服务器/// </summary>public void Stop(){_isRunning = false;foreach (var client in _clients){client.Close();}_clients.Clear();_listener?.Stop();Console.WriteLine("TCP服务器已停止");}
}

TCP客户端实现

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;public class TcpClientHelper
{private TcpClient _client;private NetworkStream _stream;private bool _isConnected;public event Action<string> OnMessageReceived;public event Action OnConnected;public event Action OnDisconnected;/// <summary>/// 连接到服务器/// </summary>public async Task<bool> ConnectAsync(string serverIp, int port, int timeoutMs = 5000){try{_client = new TcpClient();// 设置超时var connectTask = _client.ConnectAsync(serverIp, port);if (await Task.WhenAny(connectTask, Task.Delay(timeoutMs)) == connectTask){_stream = _client.GetStream();_isConnected = true;Console.WriteLine($"已连接到服务器: {serverIp}:{port}");OnConnected?.Invoke();// 开始接收数据_ = Task.Run(() => ReceiveDataAsync());return true;}else{Console.WriteLine("连接超时");_client?.Close();return false;}}catch (Exception ex){Console.WriteLine($"连接失败: {ex.Message}");return false;}}/// <summary>/// 接收数据/// </summary>private async Task ReceiveDataAsync(){var buffer = new byte[4096];try{while (_isConnected && _client.Connected){var bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead == 0){// 服务器断开连接break;}var message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到服务器消息: {message}");OnMessageReceived?.Invoke(message);}}catch (Exception ex){Console.WriteLine($"接收数据错误: {ex.Message}");}finally{Disconnect();}}/// <summary>/// 发送数据/// </summary>public async Task<bool> SendAsync(string message){if (!_isConnected || _stream == null){Console.WriteLine("未连接到服务器");return false;}try{var data = Encoding.UTF8.GetBytes(message);await _stream.WriteAsync(data, 0, data.Length);Console.WriteLine($"发送消息: {message}");return true;}catch (Exception ex){Console.WriteLine($"发送失败: {ex.Message}");return false;}}/// <summary>/// 发送字节数据/// </summary>public async Task<bool> SendBytesAsync(byte[] data){if (!_isConnected || _stream == null)return false;try{await _stream.WriteAsync(data, 0, data.Length);return true;}catch{return false;}}/// <summary>/// 断开连接/// </summary>public void Disconnect(){if (_isConnected){_isConnected = false;_stream?.Close();_client?.Close();Console.WriteLine("已断开连接");OnDisconnected?.Invoke();}}
}

UDP通信实现

UDP发送端

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;public class UdpSender
{private UdpClient _udpClient;private IPEndPoint _remoteEndPoint;/// <summary>/// 初始化UDP发送端/// </summary>public UdpSender(string remoteIp, int remotePort){_udpClient = new UdpClient();_remoteEndPoint = new IPEndPoint(IPAddress.Parse(remoteIp), remotePort);}/// <summary>/// 发送文本消息/// </summary>public async Task SendMessageAsync(string message){try{var data = Encoding.UTF8.GetBytes(message);await _udpClient.SendAsync(data, data.Length, _remoteEndPoint);Console.WriteLine($"UDP发送: {message}");}catch (Exception ex){Console.WriteLine($"UDP发送失败: {ex.Message}");}}/// <summary>/// 发送字节数据/// </summary>public async Task SendBytesAsync(byte[] data){try{await _udpClient.SendAsync(data, data.Length, _remoteEndPoint);Console.WriteLine($"UDP发送 {data.Length} 字节");}catch (Exception ex){Console.WriteLine($"UDP发送失败: {ex.Message}");}}/// <summary>/// 广播消息/// </summary>public async Task BroadcastAsync(string message, int port){try{using (var client = new UdpClient()){client.EnableBroadcast = true;var data = Encoding.UTF8.GetBytes(message);var broadcastEp = new IPEndPoint(IPAddress.Broadcast, port);await client.SendAsync(data, data.Length, broadcastEp);Console.WriteLine($"广播消息: {message}");}}catch (Exception ex){Console.WriteLine($"广播失败: {ex.Message}");}}public void Close(){_udpClient?.Close();}
}

UDP接收端

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;public class UdpReceiver
{private UdpClient _udpClient;private bool _isRunning;private int _port;public event Action<string, IPEndPoint> OnMessageReceived;/// <summary>/// 启动UDP接收/// </summary>public async Task StartAsync(int port){_port = port;_udpClient = new UdpClient(port);_isRunning = true;Console.WriteLine($"UDP接收器启动在端口: {port}");while (_isRunning){try{var result = await _udpClient.ReceiveAsync();var message = Encoding.UTF8.GetString(result.Buffer);Console.WriteLine($"UDP收到 [{result.RemoteEndPoint}]: {message}");OnMessageReceived?.Invoke(message, result.RemoteEndPoint);}catch (Exception ex){if (_isRunning){Console.WriteLine($"UDP接收错误: {ex.Message}");}}}}/// <summary>/// 停止接收/// </summary>public void Stop(){_isRunning = false;_udpClient?.Close();Console.WriteLine("UDP接收器已停止");}
}

工业以太网协议

协议栈对比

标准以太网栈              工业以太网栈(如Modbus TCP)
+-------------+          +-------------------+
|   应用层     |          | Modbus应用协议    |
+-------------+          +-------------------+
|   传输层     |          |     TCP/IP        |
+-------------+          +-------------------+
|   网络层     |          |       IP          |
+-------------+          +-------------------+
|  数据链路层   |          |     以太网         |
+-------------+          +-------------------+

Modbus TCP实现

Modbus TCP协议结构

MBAP头部 (7字节) + PDU (功能码 + 数据)

+------+------+------+------+------+------+------+----------+
| 事务  | 协议  | 长度  | 单元  | 功能  |      数据域       |
| ID   | ID   |      | ID   | 码   |                  |
| (2)  | (2)  | (2)  | (1)  | (1)  |     (0-252)      |
+------+------+------+------+------+----------+

Modbus TCP客户端(主站)

using System;
using System.Net.Sockets;
using System.Threading.Tasks;public class ModbusTcpClient
{private TcpClient _client;private NetworkStream _stream;private ushort _transactionId = 0;private readonly object _lockObj = new object();/// <summary>/// 连接Modbus TCP服务器/// </summary>public async Task<bool> ConnectAsync(string ipAddress, int port = 502){try{_client = new TcpClient();await _client.ConnectAsync(ipAddress, port);_stream = _client.GetStream();Console.WriteLine($"已连接到Modbus服务器: {ipAddress}:{port}");return true;}catch (Exception ex){Console.WriteLine($"连接失败: {ex.Message}");return false;}}/// <summary>/// 读保持寄存器 (功能码0x03)/// </summary>public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort count){// 构建Modbus请求var request = BuildRequest(slaveId, 0x03, BitConverter.GetBytes(ReverseBytes(startAddress)).Concat(BitConverter.GetBytes(ReverseBytes(count))).ToArray());// 发送请求await _stream.WriteAsync(request, 0, request.Length);// 接收响应var response = new byte[9 + count * 2]; // MBAP(7) + FC(1) + ByteCount(1) + Dataawait _stream.ReadAsync(response, 0, response.Length);// 解析数据return ParseRegisters(response, count);}/// <summary>/// 写单个保持寄存器 (功能码0x06)/// </summary>public async Task<bool> WriteSingleRegisterAsync(byte slaveId, ushort address, ushort value){var data = new byte[4];Array.Copy(BitConverter.GetBytes(ReverseBytes(address)), 0, data, 0, 2);Array.Copy(BitConverter.GetBytes(ReverseBytes(value)), 0, data, 2, 2);var request = BuildRequest(slaveId, 0x06, data);await _stream.WriteAsync(request, 0, request.Length);var response = new byte[12]; // MBAP(7) + FC(1) + Address(2) + Value(2)await _stream.ReadAsync(response, 0, response.Length);return response[7] == 0x06; // 检查功能码}/// <summary>/// 写多个保持寄存器 (功能码0x10)/// </summary>public async Task<bool> WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] values){var count = (ushort)values.Length;var byteCount = (byte)(count * 2);var data = new byte[5 + byteCount];Array.Copy(BitConverter.GetBytes(ReverseBytes(startAddress)), 0, data, 0, 2);Array.Copy(BitConverter.GetBytes(ReverseBytes(count)), 0, data, 2, 2);data[4] = byteCount;for (int i = 0; i < count; i++){Array.Copy(BitConverter.GetBytes(ReverseBytes(values[i])), 0, data, 5 + i * 2, 2);}var request = BuildRequest(slaveId, 0x10, data);await _stream.WriteAsync(request, 0, request.Length);var response = new byte[12];await _stream.ReadAsync(response, 0, response.Length);return response[7] == 0x10;}/// <summary>/// 读线圈状态 (功能码0x01)/// </summary>public async Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort count){var request = BuildRequest(slaveId, 0x01,BitConverter.GetBytes(ReverseBytes(startAddress)).Concat(BitConverter.GetBytes(ReverseBytes(count))).ToArray());await _stream.WriteAsync(request, 0, request.Length);var byteCount = (count + 7) / 8;var response = new byte[9 + byteCount];await _stream.ReadAsync(response, 0, response.Length);return ParseCoils(response, count);}/// <summary>/// 写单个线圈 (功能码0x05)/// </summary>public async Task<bool> WriteSingleCoilAsync(byte slaveId, ushort address, bool value){var coilValue = value ? (ushort)0xFF00 : (ushort)0x0000;var data = new byte[4];Array.Copy(BitConverter.GetBytes(ReverseBytes(address)), 0, data, 0, 2);Array.Copy(BitConverter.GetBytes(ReverseBytes(coilValue)), 0, data, 2, 2);var request = BuildRequest(slaveId, 0x05, data);await _stream.WriteAsync(request, 0, request.Length);var response = new byte[12];await _stream.ReadAsync(response, 0, response.Length);return response[7] == 0x05;}/// <summary>/// 构建Modbus TCP请求/// </summary>private byte[] BuildRequest(byte slaveId, byte functionCode, byte[] data){lock (_lockObj){_transactionId++;}var length = (ushort)(data.Length + 2); // 单元ID + 功能码 + 数据var request = new byte[7 + 1 + data.Length];// MBAP头部Array.Copy(BitConverter.GetBytes(ReverseBytes(_transactionId)), 0, request, 0, 2); // 事务IDArray.Copy(BitConverter.GetBytes((ushort)0), 0, request, 2, 2);                    // 协议IDArray.Copy(BitConverter.GetBytes(ReverseBytes(length)), 0, request, 4, 2);        // 长度request[6] = slaveId;                                                              // 单元ID// PDUrequest[7] = functionCode;Array.Copy(data, 0, request, 8, data.Length);return request;}/// <summary>/// 解析寄存器数据/// </summary>private ushort[] ParseRegisters(byte[] response, ushort count){var byteCount = response[8];var registers = new ushort[count];for (int i = 0; i < count; i++){registers[i] = (ushort)((response[9 + i * 2] << 8) | response[10 + i * 2]);}return registers;}/// <summary>/// 解析线圈数据/// </summary>private bool[] ParseCoils(byte[] response, ushort count){var byteCount = response[8];var coils = new bool[count];for (int i = 0; i < count; i++){var byteIndex = i / 8;var bitIndex = i % 8;coils[i] = (response[9 + byteIndex] & (1 << bitIndex)) != 0;}return coils;}/// <summary>/// 字节序转换 (大端/小端)/// </summary>private ushort ReverseBytes(ushort value){return (ushort)((value >> 8) | (value << 8));}/// <summary>/// 断开连接/// </summary>public void Disconnect(){_stream?.Close();_client?.Close();Console.WriteLine("已断开Modbus连接");}
}

Modbus TCP使用示例

using System;
using System.Linq;
using System.Threading.Tasks;public class ModbusExample
{public static async Task RunModbusDemo(){var client = new ModbusTcpClient();// 连接到Modbus服务器if (await client.ConnectAsync("192.168.1.100", 502)){try{// 读取保持寄存器Console.WriteLine("\n--- 读取保持寄存器 ---");var registers = await client.ReadHoldingRegistersAsync(1, 0, 10);for (int i = 0; i < registers.Length; i++){Console.WriteLine($"寄存器 {i}: {registers[i]}");}// 写单个寄存器Console.WriteLine("\n--- 写入单个寄存器 ---");bool success = await client.WriteSingleRegisterAsync(1, 0, 1234);Console.WriteLine($"写入结果: {(success ? "成功" : "失败")}");// 写多个寄存器Console.WriteLine("\n--- 写入多个寄存器 ---");var values = new ushort[] { 100, 200, 300, 400, 500 };success = await client.WriteMultipleRegistersAsync(1, 10, values);Console.WriteLine($"写入结果: {(success ? "成功" : "失败")}");// 读取线圈Console.WriteLine("\n--- 读取线圈 ---");var coils = await client.ReadCoilsAsync(1, 0, 16);for (int i = 0; i < coils.Length; i++){Console.WriteLine($"线圈 {i}: {coils[i]}");}// 写入线圈Console.WriteLine("\n--- 写入线圈 ---");success = await client.WriteSingleCoilAsync(1, 0, true);Console.WriteLine($"写入结果: {(success ? "成功" : "失败")}");}finally{client.Disconnect();}}}
}

EtherNet/IP实现

EtherNet/IP协议简介

EtherNet/IP是基于CIP(Common Industrial Protocol)的工业以太网协议,主要用于罗克韦尔自动化(Rockwell Automation)的设备。

封装结构:

+------------------+
| Encapsulation    |
| Header (24字节)   |
+------------------+
| Command Data     |
+------------------+

EtherNet/IP封装实现

using System;
using System.Net.Sockets;
using System.Threading.Tasks;public class EtherNetIPClient
{private TcpClient _client;private NetworkStream _stream;private uint _sessionHandle = 0;private uint _senderContext = 0;// EIP命令private const ushort CMD_NOP = 0x0000;private const ushort CMD_LIST_SERVICES = 0x0004;private const ushort CMD_LIST_IDENTITY = 0x0063;private const ushort CMD_LIST_INTERFACES = 0x0064;private const ushort CMD_REGISTER_SESSION = 0x0065;private const ushort CMD_UNREGISTER_SESSION = 0x0066;private const ushort CMD_SEND_RR_DATA = 0x006F;private const ushort CMD_SEND_UNIT_DATA = 0x0070;/// <summary>/// 连接到EtherNet/IP设备/// </summary>public async Task<bool> ConnectAsync(string ipAddress, int port = 44818){try{_client = new TcpClient();await _client.ConnectAsync(ipAddress, port);_stream = _client.GetStream();Console.WriteLine($"已连接到EIP设备: {ipAddress}:{port}");// 注册会话return await RegisterSessionAsync();}catch (Exception ex){Console.WriteLine($"连接失败: {ex.Message}");return false;}}/// <summary>/// 注册会话/// </summary>private async Task<bool> RegisterSessionAsync(){// 构建注册会话请求var request = new byte[28];// Encapsulation HeaderWriteUInt16(request, 0, CMD_REGISTER_SESSION);  // CommandWriteUInt16(request, 2, 4);                     // LengthWriteUInt32(request, 4, _sessionHandle);        // Session HandleWriteUInt32(request, 8, 0);                     // StatusWriteUInt64(request, 12, _senderContext);       // Sender ContextWriteUInt32(request, 20, 0);                    // Options// Command Specific DataWriteUInt16(request, 24, 1);                    // Protocol VersionWriteUInt16(request, 26, 0);                    // Options Flagsawait _stream.WriteAsync(request, 0, request.Length);// 接收响应var response = new byte[28];await _stream.ReadAsync(response, 0, response.Length);_sessionHandle = ReadUInt32(response, 4);Console.WriteLine($"会话注册成功, Session Handle: 0x{_sessionHandle:X8}");return true;}/// <summary>/// 列出设备标识/// </summary>public async Task<DeviceIdentity> ListIdentityAsync(){var request = new byte[24];WriteUInt16(request, 0, CMD_LIST_IDENTITY);WriteUInt16(request, 2, 0);WriteUInt32(request, 4, 0);WriteUInt32(request, 8, 0);WriteUInt64(request, 12, _senderContext++);WriteUInt32(request, 20, 0);await _stream.WriteAsync(request, 0, request.Length);var response = new byte[1024];var bytesRead = await _stream.ReadAsync(response, 0, response.Length);return ParseIdentity(response, bytesRead);}/// <summary>/// 读取标签数据 (简化版)/// </summary>public async Task<byte[]> ReadTagAsync(string tagName){// 构建CIP请求var cipRequest = BuildCIPReadRequest(tagName);var request = BuildSendRRDataRequest(cipRequest);await _stream.WriteAsync(request, 0, request.Length);var response = new byte[1024];var bytesRead = await _stream.ReadAsync(response, 0, response.Length);return ParseCIPResponse(response, bytesRead);}/// <summary>/// 构建CIP读取请求/// </summary>private byte[] BuildCIPReadRequest(string tagName){// CIP Read Tag Service (0x4C)var tagBytes = System.Text.Encoding.ASCII.GetBytes(tagName);var request = new byte[2 + tagBytes.Length + (tagBytes.Length % 2)];request[0] = 0x4C; // Read Tag Servicerequest[1] = (byte)(tagBytes.Length / 2);Array.Copy(tagBytes, 0, request, 2, tagBytes.Length);return request;}/// <summary>/// 构建SendRRData请求/// </summary>private byte[] BuildSendRRDataRequest(byte[] cipData){var dataLength = cipData.Length + 16; // CPF数据长度var request = new byte[24 + dataLength];// Encapsulation HeaderWriteUInt16(request, 0, CMD_SEND_RR_DATA);WriteUInt16(request, 2, (ushort)dataLength);WriteUInt32(request, 4, _sessionHandle);WriteUInt32(request, 8, 0);WriteUInt64(request, 12, _senderContext++);WriteUInt32(request, 20, 0);// CPF (Common Packet Format)WriteUInt32(request, 24, 0);                    // Interface HandleWriteUInt16(request, 28, 0);                    // TimeoutWriteUInt16(request, 30, 2);                    // Item Count// Null Address ItemWriteUInt16(request, 32, 0);                    // Type IDWriteUInt16(request, 34, 0);                    // Length// Unconnected Data ItemWriteUInt16(request, 36, 0xB2);                 // Type IDWriteUInt16(request, 38, (ushort)cipData.Length);Array.Copy(cipData, 0, request, 40, cipData.Length);return request;}/// <summary>/// 解析CIP响应/// </summary>private byte[] ParseCIPResponse(byte[] response, int length){// 简化的解析逻辑var dataStart = 44; // 跳过封装头和CPFvar dataLength = length - dataStart;var data = new byte[dataLength];Array.Copy(response, dataStart, data, 0, dataLength);return data;}/// <summary>/// 解析设备标识/// </summary>private DeviceIdentity ParseIdentity(byte[] response, int length){var identity = new DeviceIdentity();// 简化的解析if (length > 63){identity.VendorId = ReadUInt16(response, 48);identity.DeviceType = ReadUInt16(response, 50);identity.ProductCode = ReadUInt16(response, 52);identity.Revision = $"{response[54]}.{response[55]}";identity.Status = ReadUInt16(response, 56);identity.SerialNumber = ReadUInt32(response, 58);var nameLength = response[62];identity.ProductName = System.Text.Encoding.ASCII.GetString(response, 63, nameLength);}return identity;}/// <summary>/// 注销会话/// </summary>public async Task UnregisterSessionAsync(){if (_sessionHandle == 0) return;var request = new byte[24];WriteUInt16(request, 0, CMD_UNREGISTER_SESSION);WriteUInt32(request, 4, _sessionHandle);await _stream.WriteAsync(request, 0, request.Length);Console.WriteLine("会话已注销");}public void Disconnect(){_stream?.Close();_client?.Close();}// 辅助方法private void WriteUInt16(byte[] buffer, int offset, ushort value){buffer[offset] = (byte)(value & 0xFF);buffer[offset + 1] = (byte)((value >> 8) & 0xFF);}private void WriteUInt32(byte[] buffer, int offset, uint value){buffer[offset] = (byte)(value & 0xFF);buffer[offset + 1] = (byte)((value >> 8) & 0xFF);buffer[offset + 2] = (byte)((value >> 16) & 0xFF);buffer[offset + 3] = (byte)((value >> 24) & 0xFF);}private void WriteUInt64(byte[] buffer, int offset, ulong value){for (int i = 0; i < 8; i++){buffer[offset + i] = (byte)((value >> (i * 8)) & 0xFF);}}private ushort ReadUInt16(byte[] buffer, int offset){return (ushort)(buffer[offset] | (buffer[offset + 1] << 8));}private uint ReadUInt32(byte[] buffer, int offset){return (uint)(buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24));}
}public class DeviceIdentity
{public ushort VendorId { get; set; }public ushort DeviceType { get; set; }public ushort ProductCode { get; set; }public string Revision { get; set; }public ushort Status { get; set; }public uint SerialNumber { get; set; }public string ProductName { get; set; }public override string ToString(){return $"产品名称: {ProductName}\n" +$"厂商ID: {VendorId}\n" +$"设备类型: {DeviceType}\n" +$"产品代码: {ProductCode}\n" +$"版本: {Revision}\n" +$"序列号: {SerialNumber}";}
}

实战案例

案例1:PLC数据采集系统

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;public class PLCDataCollector
{private ModbusTcpClient _modbusClient;private bool _isRunning;private int _pollingInterval = 1000; // 轮询间隔(毫秒)public event Action<Dictionary<string, object>> OnDataCollected;/// <summary>/// 启动数据采集/// </summary>public async Task StartCollectionAsync(string plcIp, int port = 502){_modbusClient = new ModbusTcpClient();if (await _modbusClient.ConnectAsync(plcIp, port)){_isRunning = true;Console.WriteLine("数据采集已启动");while (_isRunning){try{var data = await CollectDataAsync();OnDataCollected?.Invoke(data);// 显示数据Console.WriteLine($"\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 采集数据:");foreach (var item in data){Console.WriteLine($"  {item.Key}: {item.Value}");}}catch (Exception ex){Console.WriteLine($"采集错误: {ex.Message}");}await Task.Delay(_pollingInterval);}}}/// <summary>/// 采集数据/// </summary>private async Task<Dictionary<string, object>> CollectDataAsync(){var data = new Dictionary<string, object>();// 读取温度传感器 (地址 0-3)var tempRegisters = await _modbusClient.ReadHoldingRegistersAsync(1, 0, 4);data["温度1"] = ConvertToFloat(tempRegisters[0], tempRegisters[1]);data["温度2"] = ConvertToFloat(tempRegisters[2], tempRegisters[3]);// 读取压力传感器 (地址 10-11)var pressureRegisters = await _modbusClient.ReadHoldingRegistersAsync(1, 10, 2);data["压力"] = ConvertToFloat(pressureRegisters[0], pressureRegisters[1]);// 读取运行状态 (线圈 0-7)var coils = await _modbusClient.ReadCoilsAsync(1, 0, 8);data["电机运行"] = coils[0];data["报警状态"] = coils[1];data["自动模式"] = coils[2];// 读取计数器 (地址 20)var counterRegisters = await _modbusClient.ReadHoldingRegistersAsync(1, 20, 1);data["生产计数"] = counterRegisters[0];return data;}/// <summary>/// 将两个寄存器转换为浮点数/// </summary>private float ConvertToFloat(ushort high, ushort low){var bytes = new byte[4];Array.Copy(BitConverter.GetBytes(high), 0, bytes, 2, 2);Array.Copy(BitConverter.GetBytes(low), 0, bytes, 0, 2);return BitConverter.ToSingle(bytes, 0);}/// <summary>/// 停止采集/// </summary>public void Stop(){_isRunning = false;_modbusClient?.Disconnect();Console.WriteLine("数据采集已停止");}
}

案例2:多设备监控系统

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;public class MultiDeviceMonitor
{private Dictionary<string, ModbusTcpClient> _devices;public MultiDeviceMonitor(){_devices = new Dictionary<string, ModbusTcpClient>();}/// <summary>/// 添加设备/// </summary>public async Task<bool> AddDeviceAsync(string deviceName, string ipAddress, int port = 502){var client = new ModbusTcpClient();if (await client.ConnectAsync(ipAddress, port)){_devices[deviceName] = client;Console.WriteLine($"设备 [{deviceName}] 已添加");return true;}return false;}/// <summary>/// 批量读取所有设备数据/// </summary>public async Task<Dictionary<string, DeviceData>> ReadAllDevicesAsync(){var results = new Dictionary<string, DeviceData>();var tasks = new List<Task<(string name, DeviceData data)>>();foreach (var device in _devices){tasks.Add(ReadDeviceDataAsync(device.Key, device.Value));}var allResults = await Task.WhenAll(tasks);foreach (var result in allResults){results[result.name] = result.data;}return results;}/// <summary>/// 读取单个设备数据/// </summary>private async Task<(string, DeviceData)> ReadDeviceDataAsync(string deviceName, ModbusTcpClient client){var data = new DeviceData { DeviceName = deviceName };try{// 读取状态寄存器var registers = await client.ReadHoldingRegistersAsync(1, 0, 10);data.Status = registers[0];data.Speed = registers[1];data.Temperature = registers[2] / 10.0;data.Pressure = registers[3] / 100.0;data.IsOnline = true;data.LastUpdate = DateTime.Now;}catch (Exception ex){data.IsOnline = false;data.ErrorMessage = ex.Message;}return (deviceName, data);}/// <summary>/// 生成监控报告/// </summary>public void GenerateReport(Dictionary<string, DeviceData> devicesData){Console.WriteLine("\n========== 设备监控报告 ==========");Console.WriteLine($"报告时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n");var onlineDevices = devicesData.Values.Count(d => d.IsOnline);var offlineDevices = devicesData.Count - onlineDevices;Console.WriteLine($"设备总数: {devicesData.Count}");Console.WriteLine($"在线设备: {onlineDevices}");Console.WriteLine($"离线设备: {offlineDevices}\n");Console.WriteLine("设备详情:");Console.WriteLine(new string('-', 80));Console.WriteLine($"{"设备名称",-15} {"状态",-8} {"速度",-10} {"温度",-10} {"压力",-10} {"更新时间",-20}");Console.WriteLine(new string('-', 80));foreach (var device in devicesData.Values.OrderBy(d => d.DeviceName)){if (device.IsOnline){Console.WriteLine($"{device.DeviceName,-15} {"在线",-8} {device.Speed,-10} " +$"{device.Temperature,-10:F1} {device.Pressure,-10:F2} " +$"{device.LastUpdate:HH:mm:ss}");}else{Console.WriteLine($"{device.DeviceName,-15} {"离线",-8} {"-",-10} {"-",-10} " +$"{"-",-10} {device.ErrorMessage}");}}Console.WriteLine(new string('=', 80));}/// <summary>/// 关闭所有连接/// </summary>public void DisconnectAll(){foreach (var client in _devices.Values){client.Disconnect();}_devices.Clear();}
}public class DeviceData
{public string DeviceName { get; set; }public bool IsOnline { get; set; }public int Status { get; set; }public int Speed { get; set; }public double Temperature { get; set; }public double Pressure { get; set; }public DateTime LastUpdate { get; set; }public string ErrorMessage { get; set; }
}

案例3:完整的工业通信应用程序

using System;
using System.Threading.Tasks;class Program
{static async Task Main(string[] args){Console.WriteLine("===== 工业以太网通信演示程序 =====\n");// 示例1: 基础TCP通信await Demo1_BasicTcpCommunication();// 示例2: Modbus TCP通信await Demo2_ModbusTcpCommunication();// 示例3: 多设备监控await Demo3_MultiDeviceMonitor();// 示例4: 数据采集系统await Demo4_DataCollectionSystem();Console.WriteLine("\n演示完成,按任意键退出...");Console.ReadKey();}static async Task Demo1_BasicTcpCommunication(){Console.WriteLine("\n--- 示例1: 基础TCP通信 ---");var server = new TcpServer();var serverTask = server.StartAsync("127.0.0.1", 8888);await Task.Delay(500); // 等待服务器启动var client = new TcpClientHelper();if (await client.ConnectAsync("127.0.0.1", 8888)){await client.SendAsync("Hello Server!");await Task.Delay(1000);}server.Stop();client.Disconnect();}static async Task Demo2_ModbusTcpCommunication(){Console.WriteLine("\n--- 示例2: Modbus TCP通信 ---");Console.WriteLine("(需要实际的Modbus设备或模拟器)");// 如果有Modbus设备,取消下面的注释/*var client = new ModbusTcpClient();if (await client.ConnectAsync("192.168.1.100", 502)){var registers = await client.ReadHoldingRegistersAsync(1, 0, 10);Console.WriteLine($"读取到 {registers.Length} 个寄存器");client.Disconnect();}*/}static async Task Demo3_MultiDeviceMonitor(){Console.WriteLine("\n--- 示例3: 多设备监控 ---");Console.WriteLine("(需要实际的设备)");var monitor = new MultiDeviceMonitor();// 添加设备(实际使用时修改IP地址)// await monitor.AddDeviceAsync("PLC1", "192.168.1.101");// await monitor.AddDeviceAsync("PLC2", "192.168.1.102");// 读取所有设备// var data = await monitor.ReadAllDevicesAsync();// monitor.GenerateReport(data);// monitor.DisconnectAll();}static async Task Demo4_DataCollectionSystem(){Console.WriteLine("\n--- 示例4: 数据采集系统 ---");Console.WriteLine("(需要实际的PLC设备)");var collector = new PLCDataCollector();collector.OnDataCollected += (data) =>{// 处理采集到的数据// 例如:存储到数据库、显示到界面等};// 启动采集(实际使用时修改IP地址)// await collector.StartCollectionAsync("192.168.1.100");}
}

最佳实践与注意事项

1. 连接管理

// 使用连接池管理多个连接
public class ConnectionPool
{private Queue<ModbusTcpClient> _pool = new Queue<ModbusTcpClient>();private int _maxSize = 10;public async Task<ModbusTcpClient> GetConnectionAsync(){if (_pool.Count > 0)return _pool.Dequeue();var client = new ModbusTcpClient();await client.ConnectAsync("192.168.1.100", 502);return client;}public void ReturnConnection(ModbusTcpClient client){if (_pool.Count < _maxSize)_pool.Enqueue(client);elseclient.Disconnect();}
}

2. 异常处理

public async Task<ushort[]> SafeReadRegisters(ModbusTcpClient client, byte slaveId, ushort address, ushort count, int retries = 3)
{for (int i = 0; i < retries; i++){try{return await client.ReadHoldingRegistersAsync(slaveId, address, count);}catch (Exception ex){Console.WriteLine($"读取失败 (尝试 {i + 1}/{retries}): {ex.Message}");if (i == retries - 1) throw;await Task.Delay(1000);}}return null;
}

3. 性能优化

  • 批量读写操作减少网络往返
  • 使用异步操作避免阻塞
  • 合理设置超时时间
  • 实现数据缓存机制

4. 安全考虑

  • 验证输入参数
  • 使用TLS/SSL加密通信
  • 实现访问控制
  • 记录操作日志

总结

本文档详细介绍了以太网和工业以太网的基础知识,并提供了完整的C#实现代码,包括:

  1. 标准以太网通信:TCP/IP和UDP的完整实现
  2. Modbus TCP协议:工业自动化中最常用的协议
  3. EtherNet/IP协议:罗克韦尔自动化设备通信
  4. 实战案例:PLC数据采集、多设备监控等

学习建议:

  • 从基础TCP/UDP通信开始练习
  • 使用Modbus模拟器测试代码
  • 逐步扩展到实际工业设备
  • 注意异常处理和连接管理
  • 关注实时性和可靠性要求

推荐工具:

  • Modbus Poll/Slave:Modbus协议测试
  • Wireshark:网络数据包分析
  • Visual Studio:C#开发环境
  • Modbus模拟器:ModRSsim2等

参考资源

  • Modbus协议规范
  • EtherNet/IP开发者指南
  • PROFINET官方文档
  • Microsoft .NET网络编程文档
http://www.dtcms.com/a/469280.html

相关文章:

  • 14-verilog的SPI主驱动
  • vue项目安装chromedriver超时解决办法
  • 【C++】12.多态(超详解)
  • 【Linux操作系统】进程控制
  • 做实验流程图的网站广州免费核酸采集点时间
  • 网站网页设计公司电子商务公司logo
  • 潮玩盲盒抽赏小程序玩法拆解:不同视角下的增长逻辑分析
  • 使用Milvus和DeepSeek构建RAG demo
  • WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
  • wordpress 新浪微博百度网站优化外包
  • Oracle LOB使用入门和简单使用,提供学习用的测试用例!
  • Java版旅游系统/文旅系统/旅游助手/旅游攻略/公众号/小程序/app全套源码
  • 线程2---javaEE(校招)
  • [创业之路-687]:华为“1+8+N”战略以及其背后的技术栈、商业逻辑。
  • 基于大语言模型(LLM)的城市时间、空间与情感交织分析:面向智能城市的情感动态预测与空间优化
  • 眼控交互:ErgoLAB新一代人机交互方式
  • 数字货币众筹网站开发如何做高网站的浏览量
  • 网站服务器频繁掉线的主要原因是什么
  • codeigniter换服务器之后,会员登录之后又跳回登录页面的解决方法
  • VS Code 的 SSH 密钥,并将其安全地添加到服务器
  • 香港服务器速度快慢受何影响?
  • 服务器相关:什么是 alios. centos. cuda. cuda tookit. gcc. cudann. pytorch.
  • K8S(五)—— K8s中YAML文件全方位解析:语法、案例、Port详解与快速编写技巧
  • 企业网站注销流程做企业网站需要服务器么
  • k8s存储juicefs简介
  • Ansible模块介绍(接上段)
  • 河南省建设厅网站官网重庆seo务
  • 【开题答辩全过程】以 北京房屋租赁数据分析与可视化为例,包含答辩的问题和答案
  • 什么身一什么网站建设网站开发毕设任务书
  • 【八股消消乐】手撕分布式协议和算法(基础篇)