WebSocket messages will be sent to all connected
This commit is contained in:
parent
ad1b9b2ad9
commit
d6cf7ac94d
@ -43,26 +43,26 @@ export default class WebSocketService {
|
||||
}
|
||||
|
||||
set onOpen(onOpen: VoidFunction) {
|
||||
this._onOpen = onOpen;
|
||||
if (!this.ws) return;
|
||||
this.ws.onopen = onOpen;
|
||||
this._onOpen = onOpen;
|
||||
}
|
||||
|
||||
set onReceive(onReceive: MessageEventFunction) {
|
||||
this._onReceive = onReceive;
|
||||
if (!this.ws) return;
|
||||
this.ws.onmessage = onReceive;
|
||||
this._onReceive = onReceive;
|
||||
}
|
||||
|
||||
set onClose(onClose: VoidFunction) {
|
||||
this._onClose = onClose;
|
||||
if (!this.ws) return;
|
||||
this.ws.onclose = onClose;
|
||||
this._onClose = onClose;
|
||||
}
|
||||
|
||||
set onError(onError: VoidFunction) {
|
||||
this._onError = onError;
|
||||
if (!this.ws) return;
|
||||
this.ws.onerror = onError;
|
||||
this._onError = onError;
|
||||
}
|
||||
}
|
@ -3,20 +3,21 @@ import WebSocketService from "../classes/WebSocketService";
|
||||
|
||||
const ws = new WebSocketService({});
|
||||
|
||||
export const Counter: Component = () => { // TODO update values from different clients at the same time
|
||||
export const Counter: Component = () => {
|
||||
|
||||
ws.onReceive = receiveMessage;
|
||||
const [currentCount, setCurrentCount] = React.useState(0);
|
||||
|
||||
function incrementCounterAndSend() {
|
||||
setCurrentCount(currentCount + 1);
|
||||
if (ws.isOpen()) {
|
||||
ws.send(`Current count: ${currentCount}`);
|
||||
ws.send((currentCount + 1).toString());
|
||||
}
|
||||
}
|
||||
|
||||
function receiveMessage(data: MessageEvent<any>) {
|
||||
setCurrentCount(currentCount + 1);
|
||||
const count = parseInt(data.data);
|
||||
if (!isNaN(count))
|
||||
setCurrentCount(count);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -1,32 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace pacMan.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries =
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using pacMan.Interfaces;
|
||||
|
||||
namespace pacMan.Controllers;
|
||||
|
||||
@ -9,10 +9,14 @@ namespace pacMan.Controllers;
|
||||
public class WsController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<WsController> _logger;
|
||||
private readonly IWebSocketService _wsService;
|
||||
private const int BufferSize = 1024 * 4;
|
||||
|
||||
public WsController(ILogger<WsController> logger)
|
||||
public WsController(ILogger<WsController> logger, IWebSocketService wsService)
|
||||
{
|
||||
_logger = logger;
|
||||
_wsService = wsService;
|
||||
_logger.Log(LogLevel.Debug, "WebSocket Controller created");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -22,6 +26,7 @@ public class WsController : ControllerBase
|
||||
{
|
||||
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
_logger.Log(LogLevel.Information, "WebSocket connection established to {}", HttpContext.Connection.Id);
|
||||
_wsService.Add(webSocket);
|
||||
await Echo(webSocket);
|
||||
}
|
||||
else
|
||||
@ -32,37 +37,26 @@ public class WsController : ControllerBase
|
||||
|
||||
private async Task Echo(WebSocket webSocket)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
var buffer = new byte[1024 * 4];
|
||||
var buffer = new byte[BufferSize];
|
||||
WebSocketReceiveResult? result;
|
||||
do
|
||||
{
|
||||
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
_logger.Log(LogLevel.Information, "Message received from Client");
|
||||
|
||||
result = await _wsService.Receive(webSocket, buffer);
|
||||
|
||||
if (result.CloseStatus.HasValue) break;
|
||||
|
||||
var serverMsg = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(buffer));
|
||||
|
||||
await webSocket.SendAsync(
|
||||
new ArraySegment<byte>(serverMsg, 0, result.Count),
|
||||
result.MessageType,
|
||||
result.EndOfMessage, CancellationToken.None);
|
||||
|
||||
_logger.Log(LogLevel.Information, "Message sent to Client");
|
||||
await _wsService.SendToAll(buffer, result.Count);
|
||||
} while (true);
|
||||
|
||||
await webSocket.CloseAsync(
|
||||
result.CloseStatus.Value,
|
||||
result.CloseStatusDescription,
|
||||
CancellationToken.None);
|
||||
_logger.Log(LogLevel.Information, "WebSocket connection closed from {}", HttpContext.Connection.Id);
|
||||
await _wsService.Close(webSocket, result.CloseStatus.Value, result.CloseStatusDescription ?? "No reason");
|
||||
}
|
||||
catch (WebSocketException e)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "{}", e.Message);
|
||||
}
|
||||
|
||||
_wsService.Remove(webSocket);
|
||||
}
|
||||
}
|
15
pac-man-board-game/Interfaces/IWebSocketService.cs
Normal file
15
pac-man-board-game/Interfaces/IWebSocketService.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace pacMan.Interfaces;
|
||||
|
||||
public interface IWebSocketService
|
||||
{
|
||||
void Add(WebSocket webSocket);
|
||||
void Remove(WebSocket webSocket);
|
||||
Task Send(WebSocket webSocket, string message, int length);
|
||||
Task Send(WebSocket webSocket, byte[] message, int length);
|
||||
Task SendToAll(string message, int length);
|
||||
Task SendToAll(byte[] message, int length);
|
||||
Task<WebSocketReceiveResult> Receive(WebSocket webSocket, byte[] buffer);
|
||||
Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string closeStatusDescription);
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddSingleton<IWebSocketService, WebSocketService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
84
pac-man-board-game/Services/WebSocketService.cs
Normal file
84
pac-man-board-game/Services/WebSocketService.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Utils;
|
||||
|
||||
namespace pacMan.Services;
|
||||
|
||||
public class WebSocketService : IWebSocketService
|
||||
{
|
||||
private readonly ILogger<WebSocketService> _logger;
|
||||
private readonly List<WebSocket> _webSockets = new();
|
||||
|
||||
public WebSocketService(ILogger<WebSocketService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
logger.Log(LogLevel.Debug, "WebSocket Service created");
|
||||
}
|
||||
|
||||
public void Add(WebSocket webSocket)
|
||||
{
|
||||
_webSockets.Add(webSocket);
|
||||
_logger.Log(LogLevel.Debug, "WebSocket \"{}\" added to list", webSocket.GetHashCode());
|
||||
}
|
||||
|
||||
public void Remove(WebSocket webSocket)
|
||||
{
|
||||
_webSockets.Remove(webSocket);
|
||||
_logger.Log(LogLevel.Debug, "WebSocket \"{}\" removed from list", webSocket.GetHashCode());
|
||||
}
|
||||
|
||||
public async Task Send(WebSocket webSocket, string message, int length)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(message);
|
||||
await Send(webSocket, bytes, length);
|
||||
}
|
||||
|
||||
public async Task Send(WebSocket webSocket, byte[] message, int length)
|
||||
{
|
||||
var msgSegment = new ArraySegment<byte>(message, 0, length);
|
||||
await webSocket.SendAsync(
|
||||
msgSegment,
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
CancellationToken.None);
|
||||
|
||||
_logger.Log(LogLevel.Trace,
|
||||
"Message \"{}\" sent to WebSocket {}",
|
||||
message.GetString(length),
|
||||
webSocket.GetHashCode());
|
||||
}
|
||||
|
||||
public async Task SendToAll(string message, int length)
|
||||
{
|
||||
var serverMsg = Encoding.UTF8.GetBytes(message);
|
||||
await SendToAll(serverMsg, length);
|
||||
}
|
||||
|
||||
public async Task SendToAll(byte[] message, int length)
|
||||
{
|
||||
foreach (var ws in _webSockets) await Send(ws, message, length);
|
||||
|
||||
_logger.Log(LogLevel.Debug, "Message sent to all WebSockets");
|
||||
}
|
||||
|
||||
public async Task<WebSocketReceiveResult> Receive(WebSocket webSocket, byte[] buffer)
|
||||
{
|
||||
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
_logger.Log(LogLevel.Debug,
|
||||
"Message \"{}\" received from WebSocket {}",
|
||||
buffer.GetString(result.Count),
|
||||
webSocket.GetHashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus,
|
||||
string closeStatusDescription = "No reason")
|
||||
{
|
||||
await webSocket.CloseAsync(
|
||||
closeStatus,
|
||||
closeStatusDescription,
|
||||
CancellationToken.None);
|
||||
_logger.Log(LogLevel.Information, "WebSocket connection closed from {}", webSocket.GetHashCode());
|
||||
}
|
||||
}
|
11
pac-man-board-game/Utils/Extensions.cs
Normal file
11
pac-man-board-game/Utils/Extensions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System.Text;
|
||||
|
||||
namespace pacMan.Utils;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static string GetString(this byte[] bytes, int length)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, 0, length);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace pacMan;
|
||||
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user