Существует настройка, позволяющая ограничить максимальный размер принимаемых сообщений ядра сигнала:
services.AddSignalR(options =>
{
options.MaximumReceiveMessageSize = 32768;
});
Мне нужно отслеживать фактический размер входящих сообщений ядра сигнализатора, но я не нашел способа его получить. Есть какой-либо способ сделать это? Было бы здорово, если бы это было возможно внутри IHubFilter реализации.





Вы можете попробовать этот код, чтобы отслеживать фактический размер входящих основных сообщений signalr внутри реализации IHubFilter:
public class HubLogLength : IHubFilter
{
private readonly ILogger<HubLogFilter> _logger;
public HubLogLength(ILogger<HubLogFilter> logger)
{
_logger = logger;
}
public async ValueTask<object?> InvokeMethodAsync(
HubInvocationContext invocationContext,
Func<HubInvocationContext, ValueTask<object?>> next
)
{
try
{
var result = await next(invocationContext);
result = "failed";
// this means we can find the message sent from client
if (invocationContext.HubMethodArguments.Count > 0)
{
result = "succeed";
}
var arguments = invocationContext.HubMethodArguments;
// the total length of the json:
/*var contentLength = System.Text.Json.JsonSerializer.Serialize(arguments).Length;
_logger.LogInformation("message content length {ContentLength}", contentLength);*/
// actual size of incoming signalr core messages:
if (arguments.Count > 1 && arguments[1] is string message)
{
int messageLength = message.Length;
_logger.LogInformation("message content length: {MessageLength}", messageLength);
}
return result;
}
catch (Exception ex)
{
_logger.LogError($"Exception calling '{invocationContext.HubMethodName}': {ex}");
throw;
}
}
}
Результат теста:
Это невозможно при текущей реализации обработчика соединения. Получение фактического размера необработанного сообщения без повторной сериализации помешало бы базовому PipeReader нарушить логику чтения сообщений SignalR.
SignalR использует ConnectionContext для хранения отдельных данных подключения ASP.NET Core. Это, в свою очередь, использует System.IO.Pipelines, чтобы разрешить транспортировку данных по соединению, поскольку конвейеры предназначены для чтения и записи потоковых данных. Вы можете получить доступ к базовому контексту соединения через коллекцию функций, находясь внутри фильтра-концентратора:
public class MessageSizeFilter : IHubFilter
{
public async Task InvokeMethodAsync(InvokeMethodAsync invocationContext, Func<InvokeMethodAsync, Task> next)
{
var connectionContext = invocationContext.Context.Features
.Select(x => x.Value)
.OfType<ConnectionContext>()
.FirstOrDefault();
var pipeReader = connectionContext.Transport.Input;
await next(invocationContext);
}
}
Получить длину отдельного сообщения само по себе не так уж и сложно. Обычно вы используете возможности чтения из PipeReader:
var result = await pipeReader.ReadAsync();
var messageLength = result.Buffer.Length;
А вот чтение из трубы, которая уже находится в процессе чтения, начатого из другого места, затруднительно. Трубы не предназначены для использования. Если канал уже находится в состоянии чтения, он выдаст исключение при попытке запустить новый процесс чтения. Вам придется отменить или завершить исходный процесс, что повлияет на логику чтения на стороне SignalR. Вы можете самостоятельно увидеть, как выглядит процесс чтения в обработчике соединения SignalR. Обратите внимание на большой цикл while, который поддерживает работу и обрабатывает входящие сообщения по мере их поступления, читает их и пытается отправить нужному потребителю.
Хорошо, тогда, может быть, начнем читать до того, как обработчик соединения SignalR начнет свою работу? Что ж... удачи в этом. Вам придется заново реализовать обработчик соединения, поскольку нет метода подгонки, который можно было бы переопределить, поэтому вы можете просто добавить только фрагменты своего собственного кода.
ИМХО, будет проще создать проблему на GitHub, поэтому люди из MS могут хотя бы дать вам несколько советов. Мне не удалось преодолеть проблему помех, связанную с PipeReader выдачей исключения InvalidOperationException: чтение уже выполняется. Вы можете попробовать погрузиться в эту тему самостоятельно. Вот несколько хороших обсуждений на PipeReader на GitHub.
Альтернативно попробуйте запросить недостающую функциональность.
Если у вас есть возможность знать используемый способ связи и вы можете предположить, что он не изменится в зависимости от клиентов, скажем, это всегда будет JSON, тогда вы можете попробовать сериализацию и к длине, полученной из байтов JSON, добавить еще несколько для конверт сообщения, который невелик.
Спасибо за такой подробный ответ! Я создал задачу на github: github.com/dotnet/aspnetcore/issues/56338
Ваш метод получает длину второго параметра метода концентратора и только тогда, когда он имеет строковый тип. Однако я хотел бы найти общее решение, чтобы получить размер всего сообщения без дополнительной сериализации.