HttpContext.Response основной сокет неожиданно завершает работу - PullRequest
0 голосов
/ 16 января 2019

У меня есть HttpListener, который прослушивает любые запросы файлов на локальном хосте (то есть 192.168.0.10/Foobar.ext) на указанном порту (в данном случае, в частности, это поток HLS, использующий заголовочные файлы .m3u8 и .ts видеофайлы. Но система должна работать для любого типа файла).

IAsyncResult result = listener.BeginGetContext(new AsyncCallback(HttpRequestListenerCallback), listener);
result.AsyncWaitHandle.WaitOne(1);

Когда запрос сделан, обратный вызов создает HttpListenerContext для запрошенного файла (и только этого файла) и извлекает имя файла следующим образом:

HttpListenerContext context = listener.EndGetContext(result);
string fileName = context.Request.Url.AbsolutePath.Substring(1);

контекст добавляется в словарь с именем httpContexts и связывается с int commandSequenceNumber для отслеживания запросов.

Если имя файла действительно, запросы отправляются на сервер для загрузки файла. Файл загружается и помещается в байтовый массив с именем totalBufferdata. до здесь все работает отлично.

Теперь я хочу записать байтовые данные запрошенного (видео) файла обратно в ответ (HttpListenerContext.Response) контекста, в котором был запрошен файл

Для этого я использую следующий фрагмент кода (это происходит после , когда файл был полностью загружен):

HttpListenerResponse response = httpContexts[commandSequenceNumber].Response; //Get the appropriate context from the dictionary
response.ContentLength64 = totalBufferdata.Count;//set the length response body
Stream responseStream = response.OutputStream; //The stream to which the repsonse needs to be written
try
{
   currentContext.Response.OutputStream.Write(dataNoResponseHeader, 0, dataNoResponseHeader.Length);
}
catch (IOException e)
{
   Debug.LogError("Exception in writing to response::" + e);
   Debug.LogError("innerException:: " + e.InnerException);
}
currentContext.Close();//close the request and response stream

Это отправляет ответ обратно в контексте запроса (т. Е. 192.168.0.10/Foobar.ext, через тот же порт).

Теперь это работает нормально, , пока есть быстрое и надежное подключение к Интернету . Когда подключение к Интернету медленное или несовместимое, я начинаю получать исключение:

System.IO.IOException: Unable to write data to the transport connection: The socket has been shut down. ---> System.Net.Sockets.SocketException: The socket has been shut down

С внутренним исключением:

System.Net.Sockets.SocketException (0x80004005): The socket has been shut down

Я посмотрел, что HResult из 0x80004005 подтвердил до на msdn , но это просто "E_FAIL Unspecified fail", так что не повезло.

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

До сих пор мне не удавалось найти способ добраться до основного сокета HttpListener. Я также попытался привести response.OutputStream к NetworkStream и получить сокет от NetworkStream, но это преобразование недопустимо (Что смутило меня, поскольку они оба являются потоками ввода-вывода?). Думая, что это может быть закрытой проблемой, я тоже попробовал

using(Stream testStream = response.OutputStream)
{
    testStream.Write(totalBufferdata.ToArray(), 0, totalBufferdata.Count);
}

Мне кажется, что эта проблема связана с таймаутом где-то . Но Слушатель не достигает времени ожидания по умолчанию. В соответствии с для MSDN все тайм-ауты по умолчанию должны составлять 2 минуты. К чему я нигде не подхожу. И я думаю, что исключение, возвращаемое в случае тайм-аута, должно быть ObjectDisposedException Write(Byte[], Int32, Int32) was called after the stream was closed., так как я полагаю, что тайм-аут избавит от соединения, а не прервет его? Это недоразумение?

Запросы и ответы выполняются в отдельных потоках. Но запрос, который выполняется, пока ответ еще находится в процессе, ставится в очередь и ожидает завершения ответа, прежде чем он начнет новый ответ.

Есть ли способ получить и отладить базовый сокет, чтобы выяснить, почему он закрывается / падает? Или, может быть, альтернативный подход к запросу файла через localhost и ответ на него, который не использует HttpListener?

Некоторая дополнительная информация: это для приложения Unity (Unity 2019.1), использующего версию среды выполнения сценариев .Net 4.x эквивалентно, с уровнем совместимости API .Net 4.x и сценарием IL2CPP. Но ни один из классов, обрабатывающих запрос или ответ, не наследуется от monobevahiour (что даже невозможно для потоков в единице). Сборка для Android.


Щедрость закончилась, но я открою новую для каждого, кто имеет какую-либо ценную информацию!

Ответы [ 3 ]

0 голосов
/ 25 января 2019

См. В этом блоге сообщения, показывающие, как транслировать видеофайл с использованием aspnet webapi с PushStreamContent .

https://www.strathweb.com/2013/01/asynchronously-streaming-video-with-asp-net-web-api/

https://www.c -sharpcorner.com / статей / асинхронная-видео-жить потоковой-с осиной-нетто-веб-АПИС-2-0 /

0 голосов
/ 27 января 2019

Это звучит как проблема, с которой я сталкивался ранее, учитывая информацию о том, что Интернет работает медленно или ненадежно, я хотел бы предложить использовать сокет UDP вместо TCP, поскольку они не выдают исключения, когда соединение разрывается на короткое время или если во время передачи теряются небольшие объемы данных, см. здесь . API очень похож, см здесь . Возможно, переопределить это будет немного громоздко, но я думаю, что это решит вашу проблему.

Мое другое понимание состоит в том, что вы пытаетесь, что catch catch указывает, что он принимает только IOException, хотя он и перехватывает SocketException, в большинстве случаев я просто использую универсальный класс Exception, чтобы не пытаться определить, какие исключения будут брошенный откуда.

Просто измените:

catch (IOException e)

до

catch (Exception e)

И IOException, и SocketException наследуются от класса Exception, поэтому остальная часть кода остается неизменной. Надеюсь, это даст вам больше информации о проблемах.

0 голосов
/ 24 января 2019

У вас могут быть некоторые проблемы там.

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

Другое дело, что размер файла может быть слишком большим для полной записи в одном ответе пакета. Но это может произойти в любом запросе, а не только в «плохие» моменты подключения к Интернету.

Также возможно, что, поскольку интернет-соединение нестабильно, ваш "сервер" обнаруживает, что "клиент" отключен (даже кратко), поэтому закрыл сокет.

...