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] 宿主已停止"); } } }