using System.Net.WebSockets; using Microsoft.Extensions.Logging; namespace TanksServer.Helpers; /// /// Hacky class for easier semantics /// internal abstract class EasyWebSocket { private readonly TaskCompletionSource _completionSource = new(); protected readonly ILogger Logger; private readonly WebSocket _socket; private readonly Task _readLoop; private readonly ArraySegment _buffer; private int _closed; protected EasyWebSocket(WebSocket socket, ILogger logger, ArraySegment buffer) { _socket = socket; Logger = logger; _buffer = buffer; _readLoop = ReadLoopAsync(); } public Task Done => _completionSource.Task; private async Task ReadLoopAsync() { do { var response = await _socket.ReceiveAsync(_buffer, CancellationToken.None); if (response.CloseStatus.HasValue) break; await ReceiveAsync(_buffer[..response.Count]); } while (_socket.State == WebSocketState.Open); } protected abstract Task ReceiveAsync(ArraySegment buffer); protected abstract Task ClosingAsync(); protected async Task TrySendAsync(byte[] data) { if (_socket.State != WebSocketState.Open) await CloseAsync(); Logger.LogTrace("sending {} bytes of data", _buffer.Count); try { await _socket.SendAsync(data, WebSocketMessageType.Binary, true, CancellationToken.None); } catch (WebSocketException wsEx) { Logger.LogDebug(wsEx, "send failed"); } } public async Task CloseAsync( WebSocketCloseStatus status = WebSocketCloseStatus.NormalClosure, string? description = null ) { if (Interlocked.Exchange(ref _closed, 1) == 1) return; Logger.LogDebug("closing socket"); await _socket.CloseAsync(status, description, CancellationToken.None); await _readLoop; await ClosingAsync(); _completionSource.SetResult(); } }