using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System.Net; using System.Net.WebSockets; using System.Text; namespace EVCB_OCPP.WSServer.Service.WsService; public class WsSession { public WsSession(ILogger logger) { this.logger = logger; } public PathString? Path { get; set; } public string UriScheme { get; set; } public string SessionID { get; set; } public IPEndPoint Endpoint { get; internal set; } public DateTime LastActiveTime { get; set; } private WebSocket _WebSocket; public WebSocket ClientWebSocket { get => _WebSocket; set { Init(value); } } public WebSocketState State => ClientWebSocket.State; public string SecWebSocketProtocol => ClientWebSocket.SubProtocol; public SemaphoreSlim EndConnSemaphore { get; } = new SemaphoreSlim(0); //public event OCPPClientDataEventHandler m_ReceiveData; public event EventHandler SessionClosed; private CancellationTokenSource disconnectCancellationTokenSource = new CancellationTokenSource(); private Task ReceiveLoopTask; private readonly ILogger logger; private void Init(WebSocket webSocket) { _WebSocket = webSocket; LastActiveTime = DateTime.UtcNow; ReceiveLoopTask = StartReceivd(webSocket, disconnectCancellationTokenSource.Token); } private async Task StartReceivd(WebSocket webSocket, CancellationToken token) { logger.LogInformation("{id} {func} {Path} Start", SessionID, nameof(StartReceivd), Path); byte[] prevBuffer = new byte[0]; byte[] receivdBuffer = new byte[0]; int bufferExpand = 1; int receivedBytes = 0; while (!token.IsCancellationRequested) { var tempReceiveBuffer = new byte[1024 * 4]; WebSocketReceiveResult result = null; try { result = await webSocket.ReceiveAsync(new ArraySegment(tempReceiveBuffer), token); } catch (Exception e) { _ = BruteClose(e.Message); break; } LastActiveTime = DateTime.UtcNow; if (result == null || result.CloseStatus.HasValue) { //closed gracefully await GracefulClose(result.CloseStatus.Value); break; } prevBuffer = receivdBuffer; receivdBuffer = new byte[1024 * 4 * bufferExpand]; Array.Copy(prevBuffer, 0, receivdBuffer, 0, receivedBytes); Array.Copy(tempReceiveBuffer, 0, receivdBuffer, receivedBytes, result.Count); receivedBytes += result.Count; if (!result.EndOfMessage) { bufferExpand++; continue; } var received = Encoding.UTF8.GetString(receivdBuffer, 0, receivedBytes); //logger.LogInformation("{func}:{Path} {value}", nameof(StartReceivd), Path, received); HandleReceivedData(received); bufferExpand = 1; receivedBytes = 0; } } internal virtual void HandleReceivedData(string data) { } internal Task Send(string dataString) { //logger.LogInformation("{func}:{Path} {value}", nameof(Send), Path, dataString); var data = Encoding.UTF8.GetBytes(dataString); return Send(data); } internal Task Close() { return ServerClose(); } private async Task Send(byte[] data) { try { await ClientWebSocket.SendAsync(data, WebSocketMessageType.Text, endOfMessage: true, cancellationToken: disconnectCancellationTokenSource.Token); } catch (Exception e) { logger.LogInformation("{func} {Path} exception:{msg}", nameof(Send), Path, e.Message); } } private Task ServerClose() { logger.LogInformation("{func}:{Path}", nameof(ServerClose), Path); SessionClosed?.Invoke(this, "ServerShutdown"); return InternalClose(WebSocketCloseStatus.NormalClosure, "ServerShutdown"); } private Task GracefulClose(WebSocketCloseStatus closeStatus) { logger.LogInformation("{func}:{Path} {value}", nameof(GracefulClose), Path, closeStatus); SessionClosed?.Invoke(this, closeStatus.ToString()); return InternalClose(closeStatus, null); } private Task BruteClose(string description) { logger.LogInformation("{func}:{Path} {value}", nameof(ServerClose), Path, description); SessionClosed?.Invoke(this, description); return InternalClose(WebSocketCloseStatus.EndpointUnavailable, description); } private async Task InternalClose(WebSocketCloseStatus closeStatus, string description) { try { await _WebSocket.CloseAsync(closeStatus, description, default); } catch { } finally { _WebSocket.Dispose(); } disconnectCancellationTokenSource.Cancel(); EndConnSemaphore.Release(); } }