Skip to content

Commit

Permalink
Add WebSocket stat count
Browse files Browse the repository at this point in the history
Updates *maybe* a bit too often?

Resolves #3
  • Loading branch information
Simyon264 committed Apr 10, 2024
1 parent 0ea7f92 commit 8410d4d
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 3 deletions.
43 changes: 42 additions & 1 deletion Client/Components/App.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<!DOCTYPE html>
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">

<head>
Expand All @@ -19,6 +22,44 @@
<body>
<Routes/>
<script src="_framework/blazor.web.js"></script>
<script>
// This script handles the web socket connection to the server.
const websocketUrl = "@Configuration["WebSocketUrl"]";
const ws = new WebSocket(websocketUrl);
ws.onopen = () => {
console.log("WebSocket connection established.");
};
ws.onclose = () => {
console.log("WebSocket connection closed.");
};
ws.onerror = (error) => {
console.error("WebSocket error: ", error);
};
ws.onmessage = (message) => {
//console.log("WebSocket message: ", message.data);
// The only message i can currently recieve are the amount of users online. TODO: JSON messages
if (message == "1") {
$("#activeUsers").html("There is currently <em>one user</em> here.");
} else {
$("#activeUsers").html("There are currently <em>" + message.data + " users</em> here.");
}
};
window.ws = ws;
// Every 5 seconds, send a "ping" message to the server.
setInterval(() => {
if (ws.readyState !== WebSocket.OPEN) {
console.error("WebSocket connection is not open.");
return;
}
ws.send("count");
}, 1500);
// When the user navigates to a new page, close the web socket connection.
window.addEventListener("beforeunload", () => {
ws.close();
});
</script>
</body>

</html>
1 change: 1 addition & 0 deletions Client/Components/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<PageTitle>Replay viewer</PageTitle>
<h1>Replay browser for Space Station 14</h1>
<p id="activeUsers">There is currently <em>1 user</em> here.</p>
<hr/>
<SearchBar></SearchBar>
<hr/>
Expand Down
1 change: 1 addition & 0 deletions Client/Components/Pages/Search.razor
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<PageTitle>Replay viewer</PageTitle>
<h1>Replay browser for Space Station 14</h1>
<p id="activeUsers">There is currently <em>1 user</em> here.</p>
<p>Search for replays by using the search bar below</p>
<a href="/" class="btn btn-primary">Back to main page</a>
<hr/>
Expand Down
63 changes: 61 additions & 2 deletions Server/Api/DataController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using Microsoft.AspNetCore.Cors;
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Shared;

namespace Server.Api;

Expand All @@ -13,10 +18,13 @@ namespace Server.Api;
public class DataController : ControllerBase
{
private readonly ReplayDbContext _context;

public static readonly Dictionary<Guid, WebSocket> ConnectedUsers = new();
private Timer _timer;

public DataController(ReplayDbContext context)
{
_context = context;
_timer = new Timer(CheckInactiveConnections, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

Check warning on line 27 in Server/Api/DataController.cs

View workflow job for this annotation

GitHub Actions / deploy

Nullability of reference types in type of parameter 'state' of 'void DataController.CheckInactiveConnections(object state)' doesn't match the target delegate 'TimerCallback' (possibly because of nullability attributes).

Check warning on line 27 in Server/Api/DataController.cs

View workflow job for this annotation

GitHub Actions / deploy

Nullability of reference types in type of parameter 'state' of 'void DataController.CheckInactiveConnections(object state)' doesn't match the target delegate 'TimerCallback' (possibly because of nullability attributes).
}

/// <summary>
Expand All @@ -39,4 +47,55 @@ [FromQuery] string username

return Ok(completions);
}

[Route("/ws")]
public async Task Connect()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
var userId = Guid.NewGuid();
ConnectedUsers.Add(userId, webSocket);
Log.Information("User connected with ID {UserId}", userId);
await Echo(webSocket, userId);
}
else
{
HttpContext.Response.StatusCode = 400;
}
}

private async Task Echo(WebSocket webSocket, Guid userId)
{
var buffer = new byte[1024 * 4];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

while (!result.CloseStatus.HasValue)
{
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

if (Encoding.UTF8.GetString(buffer).Contains("count"))
{
var count = ConnectedUsers.Count;
var countBytes = Encoding.UTF8.GetBytes(count.ToString());
await webSocket.SendAsync(new ArraySegment<byte>(countBytes), WebSocketMessageType.Text, true, CancellationToken.None);
}

buffer = new byte[1024 * 4];
}

ConnectedUsers.Remove(userId, out _);
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

private void CheckInactiveConnections(object state)
{
foreach (var user in ConnectedUsers)
{
if (user.Value.State == WebSocketState.Open) continue;

ConnectedUsers.Remove(user.Key, out _);
Log.Information("User disconnected with ID {UserId}", user.Key);
}
}
}
7 changes: 7 additions & 0 deletions Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@

builder.Services.AddMvc();
var app = builder.Build();

var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(10),
};

app.UseWebSockets(webSocketOptions);

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
Expand Down

0 comments on commit 8410d4d

Please sign in to comment.