using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using log4net;
namespace CanSimulatorServer
{
///
/// 模拟器 TCP 宿主:监听端口、接受连接、为每连接建立会话,并持有总线注册表。
/// port=0 时由系统分配临时端口(Port 属性读取实际值,测试回环用)。
/// UI 宿主与测试 fixture 共用本类,保证服务端行为单一路径。
///
public sealed class TcpSimulatorHost : IDisposable
{
private static readonly ILog Log = LogManager.GetLogger(typeof(TcpSimulatorHost));
private TcpListener? _listener;
private readonly ConcurrentDictionary _sessions = new();
private readonly CancellationTokenSource _cts = new();
private int _acceptedCount;
public CanBusManager Manager { get; } = new();
/// 实际监听端口(StartAsync 后有效)
public int Port { get; private set; }
public int ActiveSessionCount => _sessions.Count;
public int AcceptedCount => _acceptedCount;
/// 新会话建立事件(UI 节点树订阅)
public event Action? SessionAttached;
public Task StartAsync(int port = 0, bool listenAll = false)
{
if (_listener != null) throw new InvalidOperationException("宿主已在运行");
var ip = listenAll ? IPAddress.Any : IPAddress.Loopback;
_listener = new TcpListener(ip, port);
_listener.Start();
Port = ((IPEndPoint)_listener.LocalEndpoint).Port;
Log.Info($"[SimServer] 监听 {ip}:{Port}");
_ = Task.Run(() => AcceptLoopAsync(_cts.Token));
return Task.CompletedTask;
}
private async Task AcceptLoopAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
TcpClient? client = null;
try
{
client = await _listener!.AcceptTcpClientAsync().ConfigureAwait(false);
Interlocked.Increment(ref _acceptedCount);
var session = new ServerSession(client, Manager, s => _sessions.TryRemove(s, out _));
if (_sessions.TryAdd(session, 0))
{
SessionAttached?.Invoke(session);
session.Start();
}
client = null;
}
catch (OperationCanceledException) { break; }
catch (SocketException) { break; } // listener 已停止
catch (Exception ex)
{
Log.Warn($"[SimServer] 接受连接失败: {ex.Message}");
client?.Close();
try { await Task.Delay(200, ct).ConfigureAwait(false); }
catch (OperationCanceledException) { break; }
}
}
}
public void Dispose()
{
try { _cts.Cancel(); } catch { }
try { _listener?.Stop(); } catch { }
foreach (var s in _sessions.Keys)
{
try { s.Dispose(); } catch { }
}
_sessions.Clear();
_cts.Dispose();
Log.Info("[SimServer] 宿主已停止");
}
}
}