using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using CanSimulator;
using log4net;
namespace CanSimulatorServer
{
///
/// 单连接会话:HELLO 状态机 + 命令执行(保序响应) + 帧推送(写锁串行化保序)。
/// 读循环单线程处理请求;响应与推送经同一写锁发出,客户端按到达顺序分流。
///
public sealed class ServerSession : IDisposable
{
private static readonly ILog Log = LogManager.GetLogger(typeof(ServerSession));
private readonly TcpClient _client;
private readonly CanBusManager _manager;
private readonly Action _onClosed;
private readonly CancellationTokenSource _cts = new();
private readonly SemaphoreSlim _writeLock = new(1, 1);
private string? _busName;
private CanNode? _node;
private CanCommandExecutor? _executor;
private long _droppedPushes;
/// 推送暂存门:命令处理期间路由产生的帧先入队,响应写完后再冲,
/// 保证客户端“首行必为响应、推送随后”的保序契约
private readonly object _pushGate = new();
private bool _inCommand;
private readonly System.Collections.Generic.List _heldPushes = new();
private const int HeldPushCapacity = 1000;
public string ClientTag { get; private set; } = "";
public int NodeIndex { get; private set; } = -1;
public bool Attached => _node != null;
public long DroppedPushes => Interlocked.Read(ref _droppedPushes);
/// 节点帧到达事件(UI 帧表订阅)
public event Action? FramePushed; // (bus, pushLine, nodeIndex)
/// 总线观察级帧事件(路由发生后向 UI 报告,由宿主挂接)
internal ServerSession(TcpClient client, CanBusManager manager, Action onClosed)
{
_client = client;
_manager = manager;
_onClosed = onClosed;
}
public void Start()
{
_ = Task.Run(() => RunAsync(_cts.Token));
}
private async Task RunAsync(CancellationToken ct)
{
Stream? stream = null;
try
{
stream = _client.GetStream();
while (!ct.IsCancellationRequested)
{
string? line = await SimulatorFraming.ReadFrameAsync(stream, ct).ConfigureAwait(false);
if (line == null) break; // EOF / 截断 → 会话结束
string? response = Process(line);
if (response != null)
{
await WriteAsync(response, ct).ConfigureAwait(false);
await FlushHeldPushesAsync(ct).ConfigureAwait(false); // 响应先行,补发处理期间暂存的帧
}
}
}
catch (InvalidDataException)
{
// 协议失步:断开该会话,宿主继续监听
}
catch (IOException) { }
catch (OperationCanceledException) { }
catch (Exception ex)
{
Log.Warn($"[SimServer] 会话异常结束: {ex.Message}");
}
finally
{
Close();
}
}
/// 处理一行请求,返回响应文本(无需响应时返回 null)。
private string? Process(string line)
{
if (line.StartsWith("HELLO:", StringComparison.OrdinalIgnoreCase))
return HandleHello(line);
if (line.StartsWith("DISCONNECT", StringComparison.OrdinalIgnoreCase))
{
// 显式断开:回 OK 后由 EOF/关闭收尾
return "OK:DISCONNECTED";
}
if (_node == null)
return "ERR:NOT_HANDSHAKED";
if (line.StartsWith("SEND:", StringComparison.OrdinalIgnoreCase) ||
line.StartsWith("QUERY:", StringComparison.OrdinalIgnoreCase))
{
string resp;
lock (_pushGate) { _inCommand = true; } // 处理期间的推送改走暂存
try
{
resp = _executor!.Execute(line);
}
finally
{
lock (_pushGate) { _inCommand = false; }
}
// 裸数据(非 OK/ERR 前缀)补 OK:
if (!resp.StartsWith("OK", StringComparison.Ordinal) &&
!resp.StartsWith("ERR", StringComparison.Ordinal))
resp = "OK:" + resp;
return resp;
}
return $"ERR:UNKNOWN_CMD:{line}";
}
private string? HandleHello(string line)
{
if (_node != null)
return "ERR:ALREADY_HELLO";
var parts = line.Split(':'); // HELLO:bus:node:tag
if (parts.Length < 4 || !int.TryParse(parts[2], out int nodeIndex) || nodeIndex < 0)
return "ERR:BAD_FRAME:HELLO 格式应为 HELLO:bus:nodeIndex:tag";
string bus = parts[1];
ClientTag = parts[3];
bool ok = _manager.TryAttach(bus, nodeIndex, ClientTag, OnNodeFrame, out var node, out var busInst);
if (!ok) return "ERR:NODE_BUSY";
_node = node;
_busName = bus;
NodeIndex = nodeIndex;
_executor = new CanCommandExecutor(node!, busInst!);
return $"OK:HELLO:{bus}:{nodeIndex}";
}
/// 总线路由到本节点的帧 → 推送(命令处理中暂存,否则即发即走)
private void OnNodeFrame(CanFrame frame)
{
string push = CanCommandExecutor.FormatPush(frame);
FramePushed?.Invoke(_busName ?? "?", push, NodeIndex);
lock (_pushGate)
{
if (_inCommand)
{
if (_heldPushes.Count >= HeldPushCapacity)
{
_heldPushes.RemoveAt(0); // 满则丢最老,由客户端 RECV_ALL 兜底补账
Interlocked.Increment(ref _droppedPushes);
}
_heldPushes.Add(push);
return;
}
}
// 空闲期:即发即走,写锁串行化保帧序;失败计入丢帧
_ = WriteAsync(push, CancellationToken.None).ContinueWith(t =>
{
if (t.IsFaulted) Interlocked.Increment(ref _droppedPushes);
}, TaskScheduler.Default);
}
private async Task FlushHeldPushesAsync(CancellationToken ct)
{
string[] pending;
lock (_pushGate)
{
if (_heldPushes.Count == 0) return;
pending = _heldPushes.ToArray();
_heldPushes.Clear();
}
foreach (var p in pending)
await WriteAsync(p, ct).ConfigureAwait(false);
}
private async Task WriteAsync(string text, CancellationToken ct)
{
byte[] frame = SimulatorFraming.Encode(text);
await _writeLock.WaitAsync(ct).ConfigureAwait(false);
try
{
if (_client.Connected)
{
var s = _client.GetStream();
await s.WriteAsync(frame, 0, frame.Length, ct).ConfigureAwait(false);
await s.FlushAsync(ct).ConfigureAwait(false);
}
}
finally
{
_writeLock.Release();
}
}
public void Close()
{
try
{
if (_node != null && _busName != null)
_manager.Detach(_busName, _node);
_node = null;
_cts.Cancel();
_client.Close();
}
catch (Exception ex)
{
Log.Debug($"[SimServer] 会话关闭异常: {ex.Message}");
}
finally
{
_onClosed(this);
}
}
public void Dispose()
{
Close();
_cts.Dispose();
_writeLock.Dispose();
}
}
}