269 lines
9.5 KiB
C#
269 lines
9.5 KiB
C#
using System;
|
||
using System.Net.Sockets;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using Serilog;
|
||
|
||
namespace Protocol376Simulator.Services
|
||
{
|
||
/// <summary>
|
||
/// 网络服务类,负责TCP连接和网络通信
|
||
/// </summary>
|
||
public class NetworkService : IDisposable
|
||
{
|
||
private readonly string _serverAddress;
|
||
private readonly int _serverPort;
|
||
private TcpClient _client;
|
||
private NetworkStream _stream;
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
private readonly string _deviceIdentifier;
|
||
|
||
/// <summary>
|
||
/// 当接收到消息时触发
|
||
/// </summary>
|
||
public event EventHandler<byte[]> MessageReceived;
|
||
|
||
/// <summary>
|
||
/// 当连接状态改变时触发
|
||
/// </summary>
|
||
public event EventHandler<ConnectionStatus> ConnectionStatusChanged;
|
||
|
||
/// <summary>
|
||
/// 当发生错误时触发
|
||
/// </summary>
|
||
public event EventHandler<Exception> ErrorOccurred;
|
||
|
||
/// <summary>
|
||
/// 连接状态枚举
|
||
/// </summary>
|
||
public enum ConnectionStatus
|
||
{
|
||
Disconnected,
|
||
Connecting,
|
||
Connected,
|
||
Failed,
|
||
Reconnecting
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否连接到服务器
|
||
/// </summary>
|
||
public bool IsConnected => _client != null && _client.Connected;
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="serverAddress">服务器地址</param>
|
||
/// <param name="serverPort">服务器端口</param>
|
||
/// <param name="deviceIdentifier">设备标识(用于日志)</param>
|
||
public NetworkService(string serverAddress, int serverPort, string deviceIdentifier)
|
||
{
|
||
_serverAddress = serverAddress;
|
||
_serverPort = serverPort;
|
||
_deviceIdentifier = deviceIdentifier;
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接到服务器
|
||
/// </summary>
|
||
public async Task ConnectAsync()
|
||
{
|
||
try
|
||
{
|
||
// 如果已有连接,先断开
|
||
await DisconnectAsync();
|
||
|
||
// 重置取消标记
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
|
||
// 触发状态改变事件
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Connecting);
|
||
|
||
// 连接服务器
|
||
_client = new TcpClient();
|
||
Log.Information("{DeviceId} 正在连接到服务器 {ServerAddress}:{ServerPort}...",
|
||
_deviceIdentifier, _serverAddress, _serverPort);
|
||
|
||
await _client.ConnectAsync(_serverAddress, _serverPort);
|
||
_stream = _client.GetStream();
|
||
|
||
Log.Information("{DeviceId} 已成功连接到服务器", _deviceIdentifier);
|
||
|
||
// 触发状态改变事件
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Connected);
|
||
|
||
// 启动接收消息任务
|
||
_ = StartReceiveMessagesAsync(_cancellationTokenSource.Token);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, "{DeviceId} 连接失败: {ErrorMessage}", _deviceIdentifier, ex.Message);
|
||
|
||
// 触发错误事件
|
||
ErrorOccurred?.Invoke(this, ex);
|
||
|
||
// 触发状态改变事件
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Failed);
|
||
|
||
// 确保清理资源
|
||
_client?.Dispose();
|
||
_client = null;
|
||
_stream = null;
|
||
|
||
// 重新抛出异常,让调用者处理
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开连接
|
||
/// </summary>
|
||
public async Task DisconnectAsync()
|
||
{
|
||
try
|
||
{
|
||
// 取消所有后台任务
|
||
_cancellationTokenSource?.Cancel();
|
||
|
||
// 关闭网络流和客户端
|
||
_stream?.Close();
|
||
_client?.Close();
|
||
|
||
// 等待一段时间确保资源释放
|
||
await Task.Delay(100);
|
||
|
||
// 触发状态改变事件
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Disconnected);
|
||
|
||
Log.Information("{DeviceId} 已断开连接", _deviceIdentifier);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, "{DeviceId} 断开连接时发生错误: {ErrorMessage}", _deviceIdentifier, ex.Message);
|
||
ErrorOccurred?.Invoke(this, ex);
|
||
}
|
||
finally
|
||
{
|
||
_stream = null;
|
||
_client = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送消息
|
||
/// </summary>
|
||
/// <param name="message">要发送的消息字节数组</param>
|
||
public async Task SendMessageAsync(byte[] message)
|
||
{
|
||
if (!IsConnected)
|
||
{
|
||
throw new InvalidOperationException("未连接到服务器,无法发送消息");
|
||
}
|
||
|
||
try
|
||
{
|
||
await _stream.WriteAsync(message, 0, message.Length);
|
||
await _stream.FlushAsync();
|
||
|
||
Log.Debug("{DeviceId} 发送消息: {HexMessage}",
|
||
_deviceIdentifier, BitConverter.ToString(message).Replace("-", " "));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, "{DeviceId} 发送消息失败: {ErrorMessage}", _deviceIdentifier, ex.Message);
|
||
ErrorOccurred?.Invoke(this, ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始接收消息的后台任务
|
||
/// </summary>
|
||
/// <param name="cancellationToken">取消标记</param>
|
||
private async Task StartReceiveMessagesAsync(CancellationToken cancellationToken)
|
||
{
|
||
byte[] buffer = new byte[1024];
|
||
|
||
try
|
||
{
|
||
while (!cancellationToken.IsCancellationRequested && IsConnected)
|
||
{
|
||
// 读取消息长度
|
||
int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
|
||
|
||
if (bytesRead > 0)
|
||
{
|
||
// 创建一个新数组,只包含实际读取的字节
|
||
byte[] receivedMessage = new byte[bytesRead];
|
||
Array.Copy(buffer, receivedMessage, bytesRead);
|
||
|
||
Log.Debug("{DeviceId} 收到消息: {HexMessage}",
|
||
_deviceIdentifier, BitConverter.ToString(receivedMessage).Replace("-", " "));
|
||
|
||
// 触发消息接收事件
|
||
MessageReceived?.Invoke(this, receivedMessage);
|
||
}
|
||
else
|
||
{
|
||
// 读取0字节表示连接已关闭
|
||
Log.Warning("{DeviceId} 连接已关闭(服务器端断开)", _deviceIdentifier);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// 任务被取消,正常退出
|
||
Log.Information("{DeviceId} 接收消息任务已取消", _deviceIdentifier);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
Log.Error(ex, "{DeviceId} 接收消息时发生错误: {ErrorMessage}", _deviceIdentifier, ex.Message);
|
||
ErrorOccurred?.Invoke(this, ex);
|
||
|
||
// 触发连接状态变更
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Failed);
|
||
}
|
||
}
|
||
|
||
// 如果不是因为取消而退出循环,表示连接已断开
|
||
if (!cancellationToken.IsCancellationRequested && _client != null)
|
||
{
|
||
// 额外检查并确认连接状态
|
||
try
|
||
{
|
||
if (_client.Client != null && !_client.Client.Poll(0, SelectMode.SelectRead) || _client.Available != 0)
|
||
{
|
||
// 客户端仍然连接
|
||
return;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 忽略额外的检查异常
|
||
}
|
||
|
||
// 连接已断开
|
||
ConnectionStatusChanged?.Invoke(this, ConnectionStatus.Disconnected);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
_cancellationTokenSource?.Cancel();
|
||
_cancellationTokenSource?.Dispose();
|
||
_stream?.Dispose();
|
||
_client?.Dispose();
|
||
|
||
_cancellationTokenSource = null;
|
||
_stream = null;
|
||
_client = null;
|
||
}
|
||
}
|
||
} |