以下是基于 C# 的面向对象设计,抽象出公共接口和协议实现,涵盖 SerialPortProtocol、TcpProtocol、HttpProtocol、WebSocketProtocol、ModbusProtocol 和 MQTTProtocol。每个协议实现为单独的项目,公共接口定义在独立项目中,使用异步编程模式(async/await)确保高效和稳定,代码结构易于扩展。以下内容包含详细的代码示例、注释和项目结构说明。
一、整体设计
1. 设计目标
2. 项目结构
3. 公共接口设计公共接口 ICommunicationProtocol 定义通用通信操作,抽象类 BaseCommunicationProtocol 提供通用功能(如日志记录)。
二、公共接口项目(Communication.Abstractions)
1. 项目结构
Communication.Abstractions/
├── ICommunicationProtocol.cs
├── BaseCommunicationProtocol.cs
2. 代码实现ICommunicationProtocol.cs csharp
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.Abstractions
{
///
/// 定义通信协议的通用接口,包含连接、读写、关闭等操作
///
public interface ICommunicationProtocol : IDisposable
{
///
/// 异步连接到目标设备
///
/// 取消令牌
/// Task
Task ConnectAsync(CancellationToken cancellationToken);
///
/// 异步读取数据
///
/// 存储读取数据的缓冲区
/// 取消令牌
/// 读取的字节数
Task ReadAsync(byte[] buffer, CancellationToken cancellationToken);
///
/// 异步写入数据
///
/// 要写入的数据
/// 取消令牌
/// Task
Task WriteAsync(byte[] data, CancellationToken cancellationToken);
///
/// 异步关闭连接
///
/// 取消令牌
/// Task
Task CloseAsync(CancellationToken cancellationToken);
///
/// 获取连接状态
///
bool IsConnected { get; }
}
}
BaseCommunicationProtocol.cscsharp
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Communication.Abstractions
{
///
/// 通信协议的抽象基类,提供通用功能(如日志记录)
///
public abstract class BaseCommunicationProtocol : ICommunicationProtocol
{
protected readonly ILogger _logger;
protected BaseCommunicationProtocol(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public abstract bool IsConnected { get; }
public abstract Task ConnectAsync(CancellationToken cancellationToken);
public abstract Task ReadAsync(byte[] buffer, CancellationToken cancellationToken);
public abstract Task WriteAsync(byte[] data, CancellationToken cancellationToken);
public abstract Task CloseAsync(CancellationToken cancellationToken);
public virtual void Dispose()
{
// 实现 IDisposable,子类需重写以释放资源
}
///
/// 记录日志的辅助方法
///
protected void LogInformation(string message)
{
_logger.LogInformation($"[{GetType().Name}] {message}");
}
protected void LogError(string message, Exception ex)
{
_logger.LogError(ex, $"[{GetType().Name}] {message}");
}
}
}
三、协议实现项目以下为每个协议的实现,均引用 Communication.Abstractions 项目,使用异步方式,包含详细注释。1. SerialPortProtocol(Communication.SerialPort)项目依赖
代码实现(SerialPortProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.SerialPort
{
public class SerialPortProtocol : BaseCommunicationProtocol
{
private readonly SerialPort _serialPort;
private readonly string _portName;
private readonly int _baudRate;
public SerialPortProtocol(string portName, int baudRate, ILogger logger)
: base(logger)
{
_portName = portName ?? throw new ArgumentNullException(nameof(portName));
_baudRate = baudRate;
_serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
}
public override bool IsConnected => _serialPort?.IsOpen ?? false;
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
try
{
if (!_serialPort.IsOpen)
{
_serialPort.Open();
LogInformation($"Connected to serial port {_portName} at {_baudRate} baud.");
}
}
catch (Exception ex)
{
LogError("Failed to connect to serial port.", ex);
throw;
}
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("Serial port is not connected.");
int bytesRead = await Task.Run(() => _serialPort.Read(buffer, 0, buffer.Length), cancellationToken);
LogInformation($"Read {bytesRead} bytes from serial port.");
return bytesRead;
}
catch (Exception ex)
{
LogError("Failed to read from serial port.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("Serial port is not connected.");
await Task.Run(() => _serialPort.Write(data, 0, data.Length), cancellationToken);
LogInformation($"Wrote {data.Length} bytes to serial port.");
}
catch (Exception ex)
{
LogError("Failed to write to serial port.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
try
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
LogInformation("Serial port closed.");
}
}
catch (Exception ex)
{
LogError("Failed to close serial port.", ex);
throw;
}
}
public override void Dispose()
{
_serialPort?.Dispose();
base.Dispose();
}
}
}
2. TcpProtocol(Communication.Tcp)项目依赖
代码实现(TcpProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.Tcp
{
public class TcpProtocol : BaseCommunicationProtocol
{
private readonly TcpClient _tcpClient;
private readonly string _host;
private readonly int _port;
private NetworkStream _stream;
public TcpProtocol(string host, int port, ILogger logger)
: base(logger)
{
_host = host ?? throw new ArgumentNullException(nameof(host));
_port = port;
_tcpClient = new TcpClient();
}
public override bool IsConnected => _tcpClient?.Connected ?? false;
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
try
{
if (!_tcpClient.Connected)
{
await _tcpClient.ConnectAsync(_host, _port, cancellationToken);
_stream = _tcpClient.GetStream();
LogInformation($"Connected to {_host}:{_port}.");
}
}
catch (Exception ex)
{
LogError("Failed to connect to TCP server.", ex);
throw;
}
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("TCP client is not connected.");
int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
LogInformation($"Read {bytesRead} bytes from TCP server.");
return bytesRead;
}
catch (Exception ex)
{
LogError("Failed to read from TCP server.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("TCP client is not connected.");
await _stream.WriteAsync(data, 0, data.Length, cancellationToken);
LogInformation($"Wrote {data.Length} bytes to TCP server.");
}
catch (Exception ex)
{
LogError("Failed to write to TCP server.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
try
{
if (_tcpClient.Connected)
{
_stream?.Close();
_tcpClient.Close();
LogInformation("TCP connection closed.");
}
}
catch (Exception ex)
{
LogError("Failed to close TCP connection.", ex);
throw;
}
}
public override void Dispose()
{
_stream?.Dispose();
_tcpClient?.Dispose();
base.Dispose();
}
}
}
3. HttpProtocol(Communication.Http)项目依赖
代码实现(HttpProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.Http
{
public class HttpProtocol : BaseCommunicationProtocol
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public HttpProtocol(string baseUrl, ILogger logger)
: base(logger)
{
_baseUrl = baseUrl ?? throw new ArgumentNullException(nameof(baseUrl));
_httpClient = new HttpClient();
}
public override bool IsConnected => true; // HTTP 是无状态协议,假设始终“连接”
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
LogInformation($"HTTP protocol initialized for {_baseUrl}.");
// HTTP 无需显式连接
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync(_baseUrl, cancellationToken);
response.EnsureSuccessStatusCode();
byte[] data = await response.Content.ReadAsByteArrayAsync(cancellationToken);
Array.Copy(data, buffer, Math.Min(data.Length, buffer.Length));
LogInformation($"Read {data.Length} bytes from {_baseUrl}.");
return data.Length;
}
catch (Exception ex)
{
LogError("Failed to read from HTTP server.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
var content = new ByteArrayContent(data);
HttpResponseMessage response = await _httpClient.PostAsync(_baseUrl, content, cancellationToken);
response.EnsureSuccessStatusCode();
LogInformation($"Wrote {data.Length} bytes to {_baseUrl}.");
}
catch (Exception ex)
{
LogError("Failed to write to HTTP server.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
LogInformation("HTTP protocol does not require explicit closing.");
// HTTP 无需显式关闭
}
public override void Dispose()
{
_httpClient?.Dispose();
base.Dispose();
}
}
}
4. WebSocketProtocol(Communication.WebSocket)项目依赖
代码实现(WebSocketProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.WebSocket
{
public class WebSocketProtocol : BaseCommunicationProtocol
{
private readonly ClientWebSocket _webSocket;
private readonly Uri _uri;
public WebSocketProtocol(string uri, ILogger logger)
: base(logger)
{
_uri = new Uri(uri ?? throw new ArgumentNullException(nameof(uri)));
_webSocket = new ClientWebSocket();
}
public override bool IsConnected => _webSocket?.State == WebSocketState.Open;
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
try
{
if (_webSocket.State != WebSocketState.Open)
{
await _webSocket.ConnectAsync(_uri, cancellationlaboratory_diagnostic_techniques
LogInformation($"Connected to WebSocket {_uri}.");
}
}
catch (Exception ex)
{
LogError("Failed to connect to WebSocket.", ex);
throw;
}
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("WebSocket is not connected.");
var result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken);
LogInformation($"Read {result.Count} bytes from WebSocket.");
return result.Count;
}
catch (Exception ex)
{
LogError("Failed to read from WebSocket.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("WebSocket is not connected.");
await _webSocket.SendAsync(new ArraySegment(data), WebSocketMessageType.Binary, true, cancellationToken);
LogInformation($"Wrote {data.Length} bytes to WebSocket.");
}
catch (Exception ex)
{
LogError("Failed to write to WebSocket.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
try
{
if (IsConnected)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken);
LogInformation("WebSocket connection closed.");
}
}
catch (Exception ex)
{
LogError("Failed to close WebSocket.", ex);
throw;
}
}
public override void Dispose()
{
_webSocket?.Dispose();
base.Dispose();
}
}
}
5. ModbusProtocol(Communication.Modbus)项目依赖
代码实现(ModbusProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using Modbus.Device;
using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.Modbus
{
public class ModbusProtocol : BaseCommunicationProtocol
{
private readonly SerialPort _serialPort;
private ModbusSerialMaster _master;
private readonly byte _slaveId;
public ModbusProtocol(string portName, int baudRate, byte slaveId, ILogger logger)
: base(logger)
{
_serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
_slaveId = slaveId;
}
public override bool IsConnected => _serialPort?.IsOpen ?? false;
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
try
{
if (!_serialPort.IsOpen)
{
_serialPort.Open();
_master = ModbusSerialMaster.CreateRtu(_serialPort);
LogInformation($"Connected to Modbus RTU on {_serialPort.PortName}.");
}
}
catch (Exception ex)
{
LogError("Failed to connect to Modbus RTU.", ex);
throw;
}
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("Modbus is not connected.");
// 读取保持寄存器(功能码 03)
ushort[] registers = await Task.Run(() => _master.ReadHoldingRegisters(_slaveId, 0, (ushort)(buffer.Length / 2)), cancellationToken);
Buffer.BlockCopy(registers, 0, buffer, 0, buffer.Length);
LogInformation($"Read {buffer.Length} bytes from Modbus RTU.");
return buffer.Length;
}
catch (Exception ex)
{
LogError("Failed to read from Modbus RTU.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("Modbus is not connected.");
ushort[] registers = new ushort[data.Length / 2];
Buffer.BlockCopy(data, 0, registers, 0, data.Length);
await Task.Run(() => _master.WriteMultipleRegisters(_slaveId, 0, registers), cancellationToken);
LogInformation($"Wrote {data.Length} bytes to Modbus RTU.");
}
catch (Exception ex)
{
LogError("Failed to write to Modbus RTU.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
try
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
LogInformation("Modbus RTU connection closed.");
}
}
catch (Exception ex)
{
LogError("Failed to close Modbus RTU.", ex);
throw;
}
}
public override void Dispose()
{
_serialPort?.Dispose();
base.Dispose();
}
}
}
6. MQTTProtocol(Communication.MQTT)项目依赖
代码实现(MqttProtocol.cs)csharp
using Communication.Abstractions;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Communication.MQTT
{
public class MqttProtocol : BaseCommunicationProtocol
{
private readonly IMqttClient _mqttClient;
private readonly string _brokerAddress;
private readonly string _topic;
public MqttProtocol(string brokerAddress, string topic, ILogger logger)
: base(logger)
{
_brokerAddress = brokerAddress ?? throw new ArgumentNullException(nameof(brokerAddress));
_topic = topic ?? throw new ArgumentNullException(nameof(topic));
var factory = new MqttFactory();
_mqttClient = factory.CreateMqttClient();
}
public override bool IsConnected => _mqttClient?.IsConnected ?? false;
public override async Task ConnectAsync(CancellationToken cancellationToken)
{
try
{
if (!_mqttClient.IsConnected)
{
var options = new MqttClientOptionsBuilder()
.WithTcpServer(_brokerAddress)
.Build();
await _mqttClient.ConnectAsync(options, cancellationToken);
LogInformation($"Connected to MQTT broker {_brokerAddress}.");
}
}
catch (Exception ex)
{
LogError("Failed to connect to MQTT broker.", ex);
throw;
}
}
public override async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("MQTT client is not connected.");
var tcs = new TaskCompletionSource();
_mqttClient.ApplicationMessageReceivedAsync += e =>
{
byte[] payload = e.ApplicationMessage.Payload;
Array.Copy(payload, buffer, Math.Min(payload.Length, buffer.Length));
tcs.SetResult(payload.Length);
return Task.CompletedTask;
};
await _mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(_topic).Build());
LogInformation($"Subscribed to MQTT topic {_topic}.");
return await tcs.Task;
}
catch (Exception ex)
{
LogError("Failed to read from MQTT.", ex);
throw;
}
}
public override async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
try
{
if (!IsConnected)
throw new InvalidOperationException("MQTT client is not connected.");
var message = new MqttApplicationMessageBuilder()
.WithTopic(_topic)
.WithPayload(data)
.Build();
await _mqttClient.PublishAsync(message, cancellationToken);
LogInformation($"Published {data.Length} bytes to MQTT topic {_topic}.");
}
catch (Exception ex)
{
LogError("Failed to write to MQTT.", ex);
throw;
}
}
public override async Task CloseAsync(CancellationToken cancellationToken)
{
try
{
if (_mqttClient.IsConnected)
{
await _mqttClient.DisconnectAsync(cancellationToken);
LogInformation("MQTT connection closed.");
}
}
catch (Exception ex)
{
LogError("Failed to close MQTT connection.", ex);
throw;
}
}
public override void Dispose()
{
_mqttClient?.Dispose();
base.Dispose();
}
}
}
四、测试项目(Communication.Tests)测试代码示例以下是一个测试程序,验证所有协议的实现。csharp
using Communication.Abstractions;
using Communication.SerialPort;
using Communication.Tcp;
using Communication.Http;
using Communication.WebSocket;
using Communication.Modbus;
using Communication.MQTT;
using Microsoft.Extensions.Logging;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
// 测试串口协议
var serialPort = new SerialPortProtocol("COM3", 9600, loggerFactory.CreateLogger());
await TestProtocolAsync(serialPort, "SerialPort");
// 测试 TCP 协议
var tcp = new TcpProtocol("127.0.0.1", 12345, loggerFactory.CreateLogger());
await TestProtocolAsync(tcp, "TCP");
// 测试 HTTP 协议
var http = new HttpProtocol("http://example.com", loggerFactory.CreateLogger());
await TestProtocolAsync(http, "HTTP");
// 测试 WebSocket 协议
var webSocket = new WebSocketProtocol("ws://echo.websocket.org", loggerFactory.CreateLogger());
await TestProtocolAsync(webSocket, "WebSocket");
// 测试 Modbus 协议
var modbus = new ModbusProtocol("COM3", 9600, 1, loggerFactory.CreateLogger());
await TestProtocolAsync(modbus, "Modbus");
// 测试 MQTT 协议
var mqtt = new MqttProtocol("broker.hivemq.com", "test/topic", loggerFactory.CreateLogger());
await TestProtocolAsync(mqtt, "MQTT");
}
static async Task TestProtocolAsync(ICommunicationProtocol protocol, string protocolName)
{
try
{
await protocol.ConnectAsync(CancellationToken.None);
Console.WriteLine($"{protocolName} connected: {protocol.IsConnected}");
byte[] data = Encoding.UTF8.GetBytes("Test Data");
await protocol.WriteAsync(data, CancellationToken.None);
byte[] buffer = new byte[1024];
int bytesRead = await protocol.ReadAsync(buffer, CancellationToken.None);
Console.WriteLine($"{protocolName} read: {Encoding.UTF8.GetString(buffer, 0, bytesRead)}");
await protocol.CloseAsync(CancellationToken.None);
Console.WriteLine($"{protocolName} closed.");
}
catch (Exception ex)
{
Console.WriteLine($"{protocolName} error: {ex.Message}");
}
finally
{
protocol.Dispose();
}
}
}
五、注意事项与优化1. 注意事项
2. 优化建议
六、扩展性
七、总结
如果需要更详细的某协议实现(如 Modbus TCP 的扩展)、特定场景优化、模拟器配置截图(需通过其他方式提供),或进一步的测试用例,请告诉我!