125 lines
4.0 KiB
C#
125 lines
4.0 KiB
C#
using System.Net;
|
|
using System.Net.WebSockets;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using CommonSocketLibrary.Common;
|
|
using ILogger = Serilog.ILogger;
|
|
|
|
namespace HermesSocketServer.Socket
|
|
{
|
|
public class WebSocketUser : IDisposable
|
|
{
|
|
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 UID { get; }
|
|
public string ApiKey { get; set; }
|
|
public string? Id { get; set; }
|
|
public string? Name { get; set; }
|
|
public bool Admin { get; set; }
|
|
public bool WebLogin { 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;
|
|
Admin = false;
|
|
WebLogin = false;
|
|
_cts = new CancellationTokenSource();
|
|
UID = Guid.NewGuid().ToString("D");
|
|
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 void Dispose()
|
|
{
|
|
_socket.Dispose();
|
|
}
|
|
|
|
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 WebSocketMessage GenerateMessage<Data>(int opcode, Data data)
|
|
{
|
|
return new WebSocketMessage()
|
|
{
|
|
OpCode = opcode,
|
|
Data = data
|
|
};
|
|
}
|
|
}
|
|
} |