c#基于ASP.NET Core实现WebAPI在阿里云上起TCP服务基于ModbuRtu协议与终端设备通信的完整过程
在物联网应用开发中,经常需要实现Web服务与终端设备之间的双向通信。本文将详细介绍如何使用ASP.NET Core开发一个部署在阿里云服务器上的Web API服务,同时建立TCP服务实现与4G终端设备的通信,通过ICCID对设备进行通道管理,以及服务器日记记录。

一、项目架构设计
1.1 系统组成
Web API层: 提供RESTful接口供小程序调用
TCP服务层: 处理与4G终端设备的通信
Modbus协议层: 实现工业设备通信协议
数据模型层: 定义系统数据结构
1.2 技术选型
.NET 6.0
ASP.NET Core Web API
TCP Socket通信
Modbus RTU over TCP协议
依赖注入框架
二、核心实现过程
2.1 项目配置与启动
首先在Program.cs中配置应用服务和启动参数:
var builder = WebApplication.CreateBuilder(args);
// 配置Kestrel使用HTTPS
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(443, listenOptions =>
{
listenOptions.UseHttps(new X509Certificate2("path/to/certificate.pfx", "password"));
});
});
// 注册Modbus服务
builder.Services.AddSingleton<ModbusRtuOverTcp>();
// 添加控制器和Swagger
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 中间件配置
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// 启动TCP服务
app.Lifetime.ApplicationStarted.Register(() =>
{
var tcpService = app.Services.GetRequiredService<ModbusRtuOverTcp>();
tcpService.StartAsync(CancellationToken.None);
});
app.Run();
2.2 TCP服务实现
创建ModbusRtuOverTcp类,实现IHostedService接口作为后台服务运行:
public class ModbusRtuOverTcp : IHostedService, IDisposable
{
private TcpListener _tcpListener;
private CancellationTokenSource _cts;
private readonly Dictionary<string, TcpClient> _ICCIDclients = new Dictionary<string, TcpClient>();
private readonly ILogger<ModbusRtuOverTcp> _logger;
private readonly SimpleHybirdLock _lock = new SimpleHybirdLock();
public ModbusRtuOverTcp(ILogger<ModbusRtuOverTcp> logger)
{
_logger = logger;
}
// 启动TCP服务
public Task StartAsync(CancellationToken cancellationToken)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_listeningTask = Task.Run(() => StartTcpServer(8092, _cts.Token));
return Task.CompletedTask;
}
// TCP服务器主循环
private async Task StartTcpServer(int port, CancellationToken token)
{
try
{
_tcpListener = new TcpListener(IPAddress.Any, port);
_tcpListener.Start();
_logger.LogInformation($"Server started on port {port}");
while (!token.IsCancellationRequested)
{
var client = await _tcpListener.AcceptTcpClientAsync(token);
client.ReceiveBufferSize = 256;
// 在新线程中处理客户端连接
Thread thread = new Thread(() => { HandleClient(client, token); });
thread.IsBackground = true;
thread.Start();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "TCP server error");
}
}
// 处理客户端连接
private void HandleClient(TcpClient client, CancellationToken token)
{
string iccid = null;
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[256];
// 读取设备标识(ICCID)
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) return;
iccid = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim('\0');
if (iccid.Length != 20) return;
// 存储或更新设备连接
lock (_lock)
{
if (_ICCIDclients.TryGetValue(iccid, out TcpClient oldClient))
{
try { oldClient.Close(); } catch { }
}
_ICCIDclients[iccid] = client;
}
_logger.LogInformation($"设备连接: {iccid}");
// 主处理循环
DateTime lastClearTime = DateTime.UtcNow;
const int clearIntervalSeconds = 60;
byte[] clearBuffer = new byte[1024];
while (client.Connected && !token.IsCancellationRequested)
{
// 定时清空缓冲区
if ((DateTime.UtcNow - lastClearTime).TotalSeconds >= clearIntervalSeconds)
{
ClearReceiveBuffer(stream, clearBuffer);
lastClearTime = DateTime.UtcNow;
}
Thread.Sleep(100); // 避免CPU过高占用
}
}
catch (Exception ex)
{
_logger.LogError(ex, "客户端处理错误");
}
finally
{
// 清理资源
if (iccid != null)
{
lock (_lock)
{
if (_ICCIDclients.TryGetValue(iccid, out TcpClient currentClient) && currentClient == client)
{
_ICCIDclients.Remove(iccid);
}
}
}
try { client.Close(); } catch { }
}
}
// 清空接收缓冲区
private void ClearReceiveBuffer(NetworkStream stream, byte[] clearBuffer)
{
try
{
while (stream.DataAvailable)
{
stream.Read(clearBuffer, 0, clearBuffer.Length);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "清空缓冲区时出错");
}
}
// 停止服务
public Task StopAsync(CancellationToken cancellationToken)
{
_cts?.Cancel();
_tcpListener?.Stop();
return Task.CompletedTask;
}
public void Dispose()
{
_tcpListener?.Stop();
_cts?.Dispose();
}
}
2.3 Modbus协议实现
实现Modbus RTU over TCP协议的核心方法:
// 统一数据发送接收方法
private bool SendData(string ICCID, byte[] sendBytes, ref byte[] response)
{
_lock.Enter();
try
{
NetworkStream _stream;
lock (_ICCIDclients)
{
if (!_ICCIDclients.ContainsKey(ICCID)) return false;
_stream = _ICCIDclients[ICCID].GetStream();
}
// 清空接收缓冲区
while (_stream.DataAvailable)
{
_stream.Read(new byte[256], 0, 256);
}
// 发送请求
_stream.Write(sendBytes, 0, sendBytes.Length);
// 接收响应
var buffer = new byte[1024];
using var ms = new MemoryStream();
var timeout = Stopwatch.StartNew();
while (timeout.ElapsedMilliseconds < ReceiveTimeout)
{
if (_stream.DataAvailable)
{
int bytesRead = _stream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, bytesRead);
// 检查最小长度和CRC
if (ms.Length >= 5 && ValidateCrc(ms.ToArray()))
{
response = ms.ToArray();
return true;
}
}
else
{
Thread.Sleep(SleepInterval);
}
}
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "发送数据失败");
return false;
}
finally
{
_lock.Leave();
}
}
// CRC校验
private bool ValidateCrc(byte[] data)
{
if (data.Length < 2) return false;
byte[] crc = Crc16(data, data.Length - 2);
return crc[0] == data[^2] && crc[1] == data[^1];
}
// 读取保持寄存器功能码03H
public byte[] ReadKeepReg(string ICCID, int iDevAdd, int iAddress, int iLength)
{
// 拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x03, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
SendCommand.Add(Crc16(SendCommand.array, 6));
// 发送并接收响应
int byteLength = iLength * 2;
byte[] response = new byte[5 + byteLength];
if (SendData(ICCID, SendCommand.array, ref response))
{
// 解析响应
if (response[1] == 0x03 && response[2] == byteLength)
{
return GetByteArray(response, 3, byteLength);
}
}
return null;
}
// 预置多个寄存器功能码10H
public bool PreSetMultiByteArray(string ICCID, int iDevAdd, int iAddress, byte[] SetValue)
{
if (SetValue == null || SetValue.Length == 0 || SetValue.Length % 2 == 1)
{
return false;
}
int RegLength = SetValue.Length / 2;
// 拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x10, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(RegLength / 256), (byte)(RegLength % 256) });
SendCommand.Add((byte)SetValue.Length);
SendCommand.Add(SetValue);
SendCommand.Add(Crc16(SendCommand.array, 7 + SetValue.Length));
// 发送并接收响应
byte[] response = new byte[8];
if (SendData(ICCID, SendCommand.array, ref response))
{
// 验证响应
byte[] b = GetByteArray(response, 0, 6);
byte[] crc = Crc16(b, 6);
return ByteArrayEquals(GetByteArray(SendCommand.array, 0, 6), b) &&
crc[0] == response[6] && crc[1] == response[7];
}
return false;
}
2.4 Web API控制器
实现RESTful API接口供外部调用:
[ApiController]
[Route("api/[controller]")]
public class TcpController : ControllerBase
{
private readonly ModbusRtuOverTcp _modbusClient;
public TcpController(ModbusRtuOverTcp modbusClient)
{
_modbusClient = modbusClient;
}
// 获取发酵室启用状态
[HttpGet("fermentation-rooms/{ICCID}/{id}/enabled")]
public IActionResult GetRoomEnabled(string ICCID, int id)
{
try
{
bool enabled = _modbusClient.GetFermentationRoomEnabled(ICCID, id);
return Ok(new { RoomId = id, Enabled = enabled });
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
// 获取故障状态
[HttpGet("fault-status/{ICCID}")]
public IActionResult GetFaultStatus(string ICCID)
{
try
{
var status = _modbusClient.ReadFaultStatus(ICCID);
return Ok(status);
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
// 获取发酵室完整状态
[HttpGet("fermentation-rooms/{ICCID}/{id}/status")]
public IActionResult GetRoomStatus(string ICCID, int id)
{
try
{
var status = _modbusClient.GetRoomStatus(ICCID, id);
return Ok(status);
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
// 发送控制命令
[HttpPost("fermentation-rooms/{ICCID}/{id}/control")]
public IActionResult SendControlCommand(string ICCID, int id, [FromBody] ControlCommandRequest command)
{
try
{
bool success = _modbusClient.SendControlCommand(ICCID, id, command);
return Ok(new { Success = success });
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
// 开门控制
[HttpPost("opendoor/{ICCID}/{id}/control")]
public IActionResult SendOpenDoorControlCommand(string ICCID, int id)
{
try
{
bool success = _modbusClient.SendControlDoorOpen(ICCID, id);
return Ok(new { Success = success });
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
// 关门控制
[HttpPost("closedoor/{ICCID}/{id}/control")]
public IActionResult SendCloseDoorControlCommand(string ICCID, int id)
{
try
{
bool success = _modbusClient.SendControlDoorClose(ICCID, id);
return Ok(new { Success = success });
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
}
2.5 数据模型定义
定义系统使用的数据模型:
public static class DataModels
{
// 控制命令请求
public class ControlCommandRequest
{
public bool LockDoor { get; set; }
public bool LockTank { get; set; }
public int BrewType { get; set; }
public bool EnableDispensing { get; set; }
public int DispenseVolume { get; set; }
}
// 故障状态
public class FaultStatus
{
public int Code { get; set; }
public string Description { get; set; }
}
// 发酵室状态
public class FermentationRoomStatus
{
public int RoomId { get; set; }
public int WorkStatus { get; set; }
public decimal Temperature { get; set; }
public int RemainingVolume { get; set; }
public bool DoorLocked { get; set; }
public bool TankLocked { get; set; }
}
// 字节顺序配置
public enum DataFormat
{
ABCD,
CDAB,
BADC,
DCBA
}
}
三、部署与配置
3.1 服务器环境配置
阿里云ECS服务器配置:
选择合适规格的ECS实例
配置安全组开放443(HTTPS)和8092(TCP)端口
申请域名并配置DNS解析
SSL证书配置:
申请SSL证书或使用自签名证书
将证书文件上传到服务器指定位置
在代码中配置证书路径和密码
3.2 应用部署

发布应用:
dotnet publish -c Release -o ./publish
部署到服务器:
将发布文件上传到服务器
配置系统服务或使用反向代理
配置HTTPS:
在Program.cs中配置Kestrel使用HTTPS
确保证书路径和密码正确
四、关键技术点与优化
4.1 连接管理优化
设备标识管理:
使用ICCID作为设备唯一标识
自动处理设备重连和旧连接清理
资源释放:
实现IDisposable接口确保资源正确释放
使用finally块确保网络连接正确关闭
4.2 通信可靠性保障
超时控制:
设置合理的接收超时时间(ReceiveTimeout)
使用Stopwatch精确控制超时
数据校验:
实现Modbus CRC校验确保数据完整性
验证响应帧格式和长度
缓冲区管理:
定期清空接收缓冲区避免数据堆积
合理设置缓冲区大小
4.3 线程安全处理
同步机制:
使用SimpleHybirdLock确保线程安全
对共享资源(_ICCIDclients)加锁访问
异常处理:
使用try-catch块捕获和处理异常
记录详细日志便于问题排查
五、总结
本文详细介绍了基于ASP.NET Core实现WebAPI与TCP终端设备通信的完整过程。通过结合Web API和TCP Socket技术,我们实现了一个既能提供RESTful接口供小程序调用,又能与4G终端设备进行实时通信的服务端系统。
该系统具有以下特点:
双向通信: 支持Web客户端与终端设备的双向数据交换
协议完整: 实现完整的Modbus RTU over TCP协议
高可靠性: 完善的异常处理和连接管理机制
易于扩展: 模块化设计便于功能扩展和设备支持
这种架构非常适合物联网应用场景,可以为各种工业设备提供远程监控和控制能力。在实际部署时,需要根据具体需求调整配置参数,并确保网络环境和安全策略正确配置。