Compare commits

..

2 Commits

Author SHA1 Message Date
758186f674 🐛 Fix swaggergen 2025-04-30 01:08:59 +08:00
littlesheep.code
3637225d23 🧱 Vide coded the websocket controller 2025-04-29 17:07:00 +00:00
2 changed files with 119 additions and 21 deletions

BIN
DysonNetwork.Sphere/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,6 +1,9 @@
using System.Collections.Concurrent;
using System.Net.WebSockets; using System.Net.WebSockets;
using DysonNetwork.Sphere.Account;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace DysonNetwork.Sphere.Connection; namespace DysonNetwork.Sphere.Connection;
@ -8,43 +11,138 @@ namespace DysonNetwork.Sphere.Connection;
[Route("/ws")] [Route("/ws")]
public class WebSocketController : ControllerBase public class WebSocketController : ControllerBase
{ {
// Concurrent dictionary to store active WebSocket connections.
// Key: Tuple (AccountId, DeviceId); Value: WebSocket and CancellationTokenSource
private static readonly ConcurrentDictionary<
(long AccountId, string DeviceId),
(WebSocket Socket, CancellationTokenSource Cts)
> ActiveConnections =
new ConcurrentDictionary<(long, string), (WebSocket, CancellationTokenSource)>();
[Route("/ws")] [Route("/ws")]
[Authorize] [Authorize]
[SwaggerIgnore]
public async Task TheGateway() public async Task TheGateway()
{ {
if (HttpContext.WebSockets.IsWebSocketRequest) if (HttpContext.WebSockets.IsWebSocketRequest)
{ {
// Get AccountId from HttpContext
if (
!HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue)
|| currentUserValue is not Account.Account currentUser
)
{
HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
long accountId = currentUser.Id;
// Verify deviceId
if (string.IsNullOrEmpty(deviceId))
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await _ConnectionEventLoop(webSocket); // Create a CancellationTokenSource for this connection
var cts = new CancellationTokenSource();
var connectionKey = (accountId, deviceId);
// Add the connection to the active connections dictionary
if (!ActiveConnections.TryAdd(connectionKey, (webSocket, cts)))
{
// Failed to add
await webSocket.CloseAsync(
WebSocketCloseStatus.InternalServerError,
"Failed to establish connection.",
CancellationToken.None
);
return;
}
try
{
await _ConnectionEventLoop(webSocket, connectionKey, cts.Token);
}
catch (Exception ex)
{
Console.WriteLine($"WebSocket Error: {ex.Message}");
}
finally
{
// Connection is closed, remove it from the active connections dictionary
ActiveConnections.TryRemove(connectionKey, out _);
cts.Dispose();
}
} }
else else
{ {
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
} }
} }
private static async Task _ConnectionEventLoop(WebSocket webSocket) private static async Task _ConnectionEventLoop(
WebSocket webSocket,
(long AccountId, string DeviceId) connectionKey,
CancellationToken cancellationToken
)
{ {
// For now, it's echo // Buffer for receiving messages.
var buffer = new byte[1024 * 4]; var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync( try
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{ {
await webSocket.SendAsync( // We don't handle receiving data, so we ignore the return.
new ArraySegment<byte>(buffer, 0, receiveResult.Count), var receiveResult = await webSocket.ReceiveAsync(
receiveResult.MessageType, new ArraySegment<byte>(buffer),
receiveResult.EndOfMessage, cancellationToken
CancellationToken.None); );
while (!receiveResult.CloseStatus.HasValue)
{
// Keep connection alive and wait for close requests
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
cancellationToken
);
}
receiveResult = await webSocket.ReceiveAsync( // Close connection
new ArraySegment<byte>(buffer), CancellationToken.None); await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
cancellationToken
);
}
catch (OperationCanceledException)
{
// Connection was canceled, close it gracefully
if (
webSocket.State != WebSocketState.Closed
&& webSocket.State != WebSocketState.Aborted
)
{
await webSocket.CloseAsync(
WebSocketCloseStatus.NormalClosure,
"Connection closed by server",
CancellationToken.None
);
}
} }
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
} }
}
// This method will be used later to send messages to specific connections
public static async Task SendMessageAsync(long accountId, string deviceId, string message)
{
if (ActiveConnections.TryGetValue((accountId, deviceId), out var connection))
{
var buffer = System.Text.Encoding.UTF8.GetBytes(message);
await connection.Socket.SendAsync(
new ArraySegment<byte>(buffer, 0, buffer.Length),
WebSocketMessageType.Text,
true,
connection.Cts.Token
);
}
}
}