Current state of the websocket server for the Hermes client
This commit is contained in:
113
Socket/WebSocketUser.cs
Normal file
113
Socket/WebSocketUser.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using HermesSocketLibrary.Socket.Data;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace HermesSocketServer.Socket
|
||||
{
|
||||
public class WebSocketUser
|
||||
{
|
||||
private readonly WebSocket _socket;
|
||||
private readonly JsonSerializerOptions _options;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IPAddress? _ipAddress;
|
||||
private CancellationTokenSource _cts;
|
||||
private bool _connected;
|
||||
|
||||
public WebSocketCloseStatus? CloseStatus { get => _socket.CloseStatus; }
|
||||
public string? CloseStatusDescription { get => _socket.CloseStatusDescription; }
|
||||
public WebSocketState State { get => _socket.State; }
|
||||
public IPAddress? IPAddress { get => _ipAddress; }
|
||||
public bool Connected { get => _connected; }
|
||||
public string? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public DateTime LastHeartbeatReceived { get; set; }
|
||||
public DateTime LastHearbeatSent { get; set; }
|
||||
public CancellationToken Token { get => _cts.Token; }
|
||||
|
||||
|
||||
public WebSocketUser(WebSocket socket, IPAddress? ipAddress, JsonSerializerOptions options, ILogger logger)
|
||||
{
|
||||
_socket = socket;
|
||||
_ipAddress = ipAddress;
|
||||
_options = options;
|
||||
_connected = true;
|
||||
_logger = logger;
|
||||
_cts = new CancellationTokenSource();
|
||||
LastHeartbeatReceived = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
public async Task Close(WebSocketCloseStatus status, string? message, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _socket.CloseAsync(status, message ?? CloseStatusDescription, token);
|
||||
}
|
||||
catch (WebSocketException wse) when (wse.Message.StartsWith("The WebSocket is in an invalid state "))
|
||||
{
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Failed to close socket.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_connected = false;
|
||||
await _cts.CancelAsync();
|
||||
_cts = new CancellationTokenSource();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Send<Data>(int opcode, Data data)
|
||||
{
|
||||
var message = GenerateMessage(opcode, data);
|
||||
var content = JsonSerializer.Serialize(message, _options);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
var array = new ArraySegment<byte>(bytes);
|
||||
var total = bytes.Length;
|
||||
var current = 0;
|
||||
|
||||
while (current < total)
|
||||
{
|
||||
var size = Encoding.UTF8.GetBytes(content.Substring(current), array);
|
||||
await _socket.SendAsync(array, WebSocketMessageType.Text, current + size >= total, Token);
|
||||
current += size;
|
||||
}
|
||||
|
||||
_logger.Verbose($"TX #{opcode}: {content}");
|
||||
}
|
||||
|
||||
public async Task<WebSocketReceiveResult?> Receive(ArraySegment<byte> bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _socket.ReceiveAsync(bytes, Token);
|
||||
}
|
||||
catch (WebSocketException wse) when (wse.Message.StartsWith("The remote party "))
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to receive a web socket message.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SocketMessage GenerateMessage<Data>(int opcode, Data data)
|
||||
{
|
||||
return new SocketMessage()
|
||||
{
|
||||
OpCode = opcode,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user