From a24f0c168177cf9209afef19447f04eabe30d163 Mon Sep 17 00:00:00 2001 From: Loretta Date: Thu, 30 Oct 2025 14:55:47 +0100 Subject: [PATCH] SignalR improvements; --- .../SignalRs/AcSignalRSendToClientService.cs | 57 +++++++++++++++++++ .../SignalRs/AcWebSignalRHubBase.cs | 29 ++++++++-- .../SignalRs/AcSignalRClientBase.cs | 20 ++++--- AyCode.Services/SignalRs/IAcSignalRHubBase.cs | 2 +- AyCode.Services/SignalRs/SendToClientType.cs | 9 +++ .../SignalRs/SignalMessageTagAttribute.cs | 21 ++++++- 6 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 AyCode.Services.Server/SignalRs/AcSignalRSendToClientService.cs create mode 100644 AyCode.Services/SignalRs/SendToClientType.cs diff --git a/AyCode.Services.Server/SignalRs/AcSignalRSendToClientService.cs b/AyCode.Services.Server/SignalRs/AcSignalRSendToClientService.cs new file mode 100644 index 0000000..45c9ad6 --- /dev/null +++ b/AyCode.Services.Server/SignalRs/AcSignalRSendToClientService.cs @@ -0,0 +1,57 @@ +using AyCode.Core.Extensions; +using AyCode.Core.Helpers; +using AyCode.Core.Loggers; +using AyCode.Services.Loggers; +using AyCode.Services.SignalRs; +using MessagePack.Resolvers; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; + +namespace AyCode.Services.Server.SignalRs; + +public abstract class AcSignalRSendToClientService(IHubContext signalRHub, IAcLoggerBase logger) //: IAcSignalRHubServer + where TSignalRHub: Hub, IAcSignalRHubServer where TSignalRTags : AcSignalRTags where TLogger : IAcLoggerBase +{ + protected IAcLoggerBase Logger => logger; + + protected virtual async Task SendMessageToClient(IAcSignalRHubItemServer sendTo, int messageTag, object? content) + { + var jsonContent = new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content); + await SendMessageToClient(sendTo, messageTag, jsonContent, null); + } + + protected virtual async Task SendMessageToClient(IAcSignalRHubItemServer sendTo, int messageTag, ISignalRMessage message, int? requestId = null) + { + var sendingDataMessagePack = message.ToMessagePack(ContractlessStandardResolver.Options); + + Logger.Info($"[{(sendingDataMessagePack.Length/1024)}kb] Server sending dataMessagePack to client; {nameof(requestId)}: {requestId}; {ConstHelper.NameByValue(messageTag)}"); + //Logger.Info($"[{(responseDataMessagePack.Length/1024)}kb] Server sending dataMessagePack to client; {nameof(requestId)}: {requestId}; ConnectionId: {signalRHub.ConnectionId}; {ConstHelper.NameByValue(messageTag)}"); + + await sendTo.OnReceiveMessage(messageTag, sendingDataMessagePack, requestId); + } + + public virtual async Task SendMessageToAllClients(int messageTag, object? content) + { + await SendMessageToClient(signalRHub.Clients.All, messageTag, content); + } + + public virtual async Task SendMessageToConnection(string connectionId, int messageTag, object? content) + { + await SendMessageToClient(signalRHub.Clients.Client(connectionId), messageTag, content); + } + + public virtual async Task SendMessageToConnections(IEnumerable connectionIds, int messageTag, object? content) + { + await SendMessageToClient(signalRHub.Clients.Clients(connectionIds), messageTag, content); + } + + public virtual async Task SendMessageToUser(string user, int messageTag, object? content) + { + await SendMessageToClient(signalRHub.Clients.User(user), messageTag, content); + } + + public virtual async Task SendMessageToUsers(IEnumerable users, int messageTag, object? content) + { + await SendMessageToClient(signalRHub.Clients.Users(users), messageTag, content); + } +} \ No newline at end of file diff --git a/AyCode.Services.Server/SignalRs/AcWebSignalRHubBase.cs b/AyCode.Services.Server/SignalRs/AcWebSignalRHubBase.cs index f61ee14..9162d35 100644 --- a/AyCode.Services.Server/SignalRs/AcWebSignalRHubBase.cs +++ b/AyCode.Services.Server/SignalRs/AcWebSignalRHubBase.cs @@ -52,9 +52,9 @@ public abstract class AcWebSignalRHubBase(IConfiguration await base.OnDisconnectedAsync(exception); } - public virtual Task OnReceiveMessage(int messageTag, byte[]? message, int? requestId) + public virtual Task OnReceiveMessage(int messageTag, byte[]? messageBytes, int? requestId) { - return ProcessOnReceiveMessage(messageTag, message, requestId, null); + return ProcessOnReceiveMessage(messageTag, messageBytes, requestId, null); } protected async Task ProcessOnReceiveMessage(int messageTag, byte[]? message, int? requestId, Func? notFoundCallback) @@ -133,14 +133,19 @@ public abstract class AcWebSignalRHubBase(IConfiguration } else Logger.Debug($"{logText}(); {tagName}"); - var responseDataJson = new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, methodInfoModel.MethodInfo.InvokeMethod(methodsByDeclaringObject.InstanceObject, paramValues)); + var responseData = methodInfoModel.MethodInfo.InvokeMethod(methodsByDeclaringObject.InstanceObject, paramValues); + var responseDataJson = new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, responseData); var responseDataJsonKiloBytes = System.Text.Encoding.Unicode.GetByteCount(responseDataJson.ResponseData!) / 1024; //File.WriteAllText(Path.Combine("h:", $"{requestId}.json"), responseDataJson.ResponseData); Logger.Info($"[{responseDataJsonKiloBytes}kb] responseData serialized to json"); + await ResponseToCaller(messageTag, responseDataJson, requestId); - + + if (methodInfoModel.Attribute.SendToOtherClientType != SendToClientType.None) + SendMessageToOtherClients(methodInfoModel.Attribute.SendToOtherClientTag, responseData).Forget(); + return; } @@ -155,15 +160,31 @@ public abstract class AcWebSignalRHubBase(IConfiguration await ResponseToCaller(messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Error), requestId); } + protected Task ResponseToCaller2(int messageTag, object? content) + => ResponseToCaller(messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content), null); + protected async Task ResponseToCaller(int messageTag, ISignalRMessage message, int? requestId) => await SendMessageToClient(Clients.Caller, messageTag, message, requestId); + protected Task SendMessageToUserId2(string userId, int messageTag, object? content) + => SendMessageToUserId(userId, messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content), null); + public async Task SendMessageToUserId(string userId, int messageTag, ISignalRMessage message, int? requestId) => await SendMessageToClient(Clients.User(userId), messageTag, message, requestId); + public async Task SendMessageToConnectionId2(string connectionId, int messageTag, object? content) + => await SendMessageToConnectionId(connectionId, messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content), null); + public async Task SendMessageToConnectionId(string connectionId, int messageTag, ISignalRMessage message, int? requestId) => await SendMessageToClient(Clients.Client(Context.ConnectionId), messageTag, message, requestId); + public async Task SendMessageToOtherClients(int messageTag, object? content) + => await SendMessageToClient(Clients.Others, messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content), null); + + public async Task SendMessageToAllClients(int messageTag, object? content) + => await SendMessageToClient(Clients.All, messageTag, new SignalResponseJsonMessage(messageTag, SignalResponseStatus.Success, content), null); + + protected async Task SendMessageToClient(IAcSignalRHubItemServer sendTo, int messageTag, ISignalRMessage message, int? requestId = null) { var responseDataMessagePack = message.ToMessagePack(ContractlessStandardResolver.Options); diff --git a/AyCode.Services/SignalRs/AcSignalRClientBase.cs b/AyCode.Services/SignalRs/AcSignalRClientBase.cs index dd1eea1..962b983 100644 --- a/AyCode.Services/SignalRs/AcSignalRClientBase.cs +++ b/AyCode.Services/SignalRs/AcSignalRClientBase.cs @@ -16,8 +16,8 @@ namespace AyCode.Services.SignalRs protected readonly HubConnection HubConnection; protected readonly AcLoggerBase Logger; - public event Action OnMessageReceived = null!; - //public event Action OnMessageRequested; + //protected event Action OnMessageReceived = null!; + protected abstract Task MessageReceived(int messageTag, byte[] messageBytes); public int Timeout = 10000; private const string TagsName = "SignalRTags"; @@ -27,9 +27,13 @@ namespace AyCode.Services.SignalRs Logger = logger; Logger.Detail(fullHubName); + //TODO: HubConnectionBuilder constructor!!! - J. HubConnection = new HubConnectionBuilder() .WithUrl(fullHubName) - //.WithAutomaticReconnect() + .WithAutomaticReconnect() + .WithStatefulReconnect() + .WithKeepAliveInterval(TimeSpan.FromSeconds(60)) + .WithServerTimeout(TimeSpan.FromSeconds(120)) //.AddMessagePackProtocol(options => { // options.SerializerOptions = MessagePackSerializerOptions.Standard // .WithResolver(MessagePack.Resolvers.StandardResolver.Instance) @@ -206,11 +210,11 @@ namespace AyCode.Services.SignalRs return SendMessageToServerAsync(messageTag, message, requestId); } - public virtual Task OnReceiveMessage(int messageTag, byte[] message, int? requestId) + public virtual Task OnReceiveMessage(int messageTag, byte[] messageBytes, int? requestId) { var logText = $"Client OnReceiveMessage; {nameof(requestId)}: {requestId}; {ConstHelper.NameByValue(TagsName, messageTag)}"; - if (message.Length == 0) Logger.Warning($"message.Length == 0! {logText}"); + if (messageBytes.Length == 0) Logger.Warning($"message.Length == 0! {logText}"); try { @@ -219,9 +223,9 @@ namespace AyCode.Services.SignalRs var reqId = requestId.Value; _responseByRequestId[reqId].ResponseDateTime = DateTime.UtcNow; - Logger.Info($"[{_responseByRequestId[reqId].ResponseDateTime.Subtract(_responseByRequestId[reqId].RequestDateTime).TotalMilliseconds:N0}ms][{(message.Length/1024)}kb]{logText}"); + Logger.Info($"[{_responseByRequestId[reqId].ResponseDateTime.Subtract(_responseByRequestId[reqId].RequestDateTime).TotalMilliseconds:N0}ms][{(messageBytes.Length/1024)}kb]{logText}"); - var responseMessage = message.MessagePackTo(ContractlessStandardResolver.Options); + var responseMessage = messageBytes.MessagePackTo(ContractlessStandardResolver.Options); switch (_responseByRequestId[reqId].ResponseByRequestId) { @@ -250,7 +254,7 @@ namespace AyCode.Services.SignalRs } else Logger.Info(logText); - OnMessageReceived(messageTag, message, requestId); + MessageReceived(messageTag, messageBytes).Forget(); } catch (Exception ex) { diff --git a/AyCode.Services/SignalRs/IAcSignalRHubBase.cs b/AyCode.Services/SignalRs/IAcSignalRHubBase.cs index 7abf086..7ec6f9c 100644 --- a/AyCode.Services/SignalRs/IAcSignalRHubBase.cs +++ b/AyCode.Services/SignalRs/IAcSignalRHubBase.cs @@ -3,5 +3,5 @@ public interface IAcSignalRHubBase { //Task OnRequestMessage(int messageTag, int requestId); - Task OnReceiveMessage(int messageTag, byte[] message, int? requestId); + Task OnReceiveMessage(int messageTag, byte[] messageBytes, int? requestId); } \ No newline at end of file diff --git a/AyCode.Services/SignalRs/SendToClientType.cs b/AyCode.Services/SignalRs/SendToClientType.cs new file mode 100644 index 0000000..702b345 --- /dev/null +++ b/AyCode.Services/SignalRs/SendToClientType.cs @@ -0,0 +1,9 @@ +namespace AyCode.Services.SignalRs; + +public enum SendToClientType +{ + None = 0, + Others = 10, + Caller = 20, + All = 30 +} \ No newline at end of file diff --git a/AyCode.Services/SignalRs/SignalMessageTagAttribute.cs b/AyCode.Services/SignalRs/SignalMessageTagAttribute.cs index 0fb1e56..4d46b7a 100644 --- a/AyCode.Services/SignalRs/SignalMessageTagAttribute.cs +++ b/AyCode.Services/SignalRs/SignalMessageTagAttribute.cs @@ -1,12 +1,31 @@ namespace AyCode.Services.SignalRs; +/// +/// +/// +/// Milyen SignalR tag-eket fogadjon a metódus [AttributeUsage(AttributeTargets.Method)] public class TagAttribute(int messageTag) : Attribute { public int MessageTag { get; init; } = messageTag; } +/// +/// +/// +/// Milyen SignalR tag-eket fogadjon a metódus. +/// Milyen Tag-el küldje ki a többi kliens-nek a response-t. +/// Kiknek küldje még el a response-t. +/// Notification message. Jelenleg nincs implementálva! [AttributeUsage(AttributeTargets.Method)] -public class SignalRAttribute(int messageTag) : TagAttribute(messageTag) +public class SignalRAttribute(int messageTag, int sendToOtherClientTag = 0, SendToClientType sendToOtherClientType = SendToClientType.None, string? sendToOtherClientNotificationMessage = null) : TagAttribute(messageTag) { + public int SendToOtherClientTag { get; init; } = sendToOtherClientTag; + public SendToClientType SendToOtherClientType { get; init; } = sendToOtherClientType; + public string? SendToOtherClientNotificationMessage { get; init; } = sendToOtherClientNotificationMessage; } + +[AttributeUsage(AttributeTargets.Method)] +public class SignalRSendToClientAttribute(int messageTag) : TagAttribute(messageTag) +{ +} \ No newline at end of file