Ответ HttpHandler никогда не возвращается

У нас есть довольно сложный httphandler для обработки изображений. Обычно он передает любую часть изображения любого запрошенного размера. Некоторые клиенты без проблем используют этот обработчик. Но у нас есть одно место, которое создает проблемы, а теперь оно создает проблемы и в моей среде разработки.

Случается, что клиент никогда ничего не получает по некоторым запросам. Таким образом, запросы 1 и 2 в порядке, но запросы 3 и 4 никогда не заканчиваются.

  • Во время отладки я вижу, что сервер готов и выполнил запрос.
  • Однако клиент все еще ожидает результата (отладка с помощью fiddler2 показывает, что ответа не было)

Код, который мы используем для потоковой передачи изображения,

        if (!context.Response.IsClientConnected)
        {
            imageStream.Close();
            imageStream.Dispose();
            return;
        }

        context.Response.BufferOutput = true;
        context.Response.ContentType = "image/" + imageformat;

        context.Response.AppendHeader("Content-Length", imageStream.Length.ToString());

        if (imageStream != null && imageStream.Length > 0 && context.Response.IsClientConnected)
            context.Response.BinaryWrite(imageStream.ToArray());

        if (context.Response.IsClientConnected)
            context.Response.Flush();

        imageStream.Close();
        imageStream.Dispose();

ImageStream - это MemoryStream с содержимым изображения.

После вызова response.Flush () мы проводим дополнительную очистку и записываем сводки в журнал событий.

Мы также вызываем GC.Collect () после каждого запроса, потому что объекты, которые мы используем в памяти, становятся очень большими. Я знаю, что это плохая практика, но может ли это доставить нам проблемы?

Проблемы с невозвращением запросов возникают как в IIS 5 (Win XP), так и в IIS 6 (Win 2003), мы используем .NET framework v2.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
3 768
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Во-первых, есть лучшие способы работы с потоками, которые используют массив для всего (например, MemoryStreamмощь здесь не нужен).

Я бы предусмотрел цикл:

const int BUFFER_SIZE = 4096; // pick your poison
bute[] buffer = new byte[BUFFER_SIZE];
int bytesRead;

while((bytesRead = inStream.Read(buffer, 0, BUFFER_SIZE)) > 0)
{
    outStream.Write(buffer, 0, bytesRead);
}

Вы также должны сделать это с отключенной буферизацией (.Response.BufferOutput = false).

Что касается проблемы, я подозреваю, что вы не написали достаточно данных / закрыли ответ (.Response.Close()).

Это не было решением, но все же полезно!

Michiel Overeem 11.11.2008 14:54

Однако, если у вас делать есть MemoryStream, есть более простой способ: MemoryStream.WriteTo (Stream)

Jon Skeet 11.11.2008 15:37

Сборка мусора не должна быть проблемой. Почему вы устанавливаете для BufferOutput значение true? Учитывая, что вы просто хотите записать данные напрямую, я бы подумал, что было бы более подходящим установить для него значение false.

Я предлагаю вам пройти диагностику на один уровень ниже: используйте Wireshark, чтобы точно увидеть, что происходит на уровне сети. Fiddler отлично подходит для уровня HTTP, но иногда вам нужно еще больше деталей.

При включенной буферизации вполне возможно, что сервер еще не отправил что-нибудь, особенно если длины не совпадают.

Marc Gravell 11.11.2008 12:38

Длина должна быть приемлемой в зависимости от того, что пишется - и есть Response.Flush (), предполагая, что клиент все еще подключен. Хотя это очень странно.

Jon Skeet 11.11.2008 13:01

Если буферизация включена, нет необходимости добавлять заголовок Content-Length, ASP.NET сделает это за вас.

AnthonyWJones 11.11.2008 16:40

Мы также использовали WebRequests для сбора дополнительной информации. Те блокировали соединения. Использование WebResponses помогло.

using (HttpWebResponse test_resp = (HttpWebResponse)test_req.GetResponse())
{
}

Я не знал, что они могут заблокировать другой запрос и т. д.

Ответ принят как подходящий

Клиент ограничивает количество одновременных запросов к любому серверу. Кроме того, при запросе от ресурса, который требует состояния сеанса (по умолчанию), другие запросы ресурсов, требующих состояния сеанса, будут блокироваться.

При использовании HttpWebResponse вы должны удалить либо этот объект, либо поток, возвращаемый его методом GetResponseStream, чтобы завершить соединение.

Ваш код был очень запутанным. Вы включили буферизацию, установили длину содержимого и использовали сброс. Это приводит к появлению некоторых странных заголовков HTTP. Обычно с включенной буферизацией вы оставляете настройку заголовка Content-Length для обработки ASP.NET.

При использовании flush ASP.NET предполагает, что впоследствии вы можете отправить больше данных. В этом случае будет использоваться передача по частям. После завершения ответа последний набор заголовков отправляется для последнего фрагмента, каждый фрагмент является заголовком своей собственной длины, и из них выводится общая длина содержимого. Первый кусок нет должен иметь заголовок Content-Length, однако ваш код добавляет этот заголовок.

Если вы отключите буферизацию и закачиваете байты в выходной поток самостоятельно потом, вы должны сами установить заголовок Content-Length, потому что эффективное отключение буфера означает, что вы берете на себя ответственность именно за то, что отправляется клиенту. Код Марка - простой пример такого насоса, хотя я бы использовал буфер большего размера, иначе в MemoryStream метод WriteTo был бы более эффективным.

Другие вопросы по теме