【问题标题】:HttpContext.Response underlying socket shuts down unexpectedlyHttpContext.Response 底层套接字意外关闭
【发布时间】:2019-06-10 09:43:47
【问题描述】:

我有一个HttpListener,它在指定端口上侦听本地主机(即 192.168.0.10/Foobar.ext)上的任何文件请求(在这种情况下,它是一个使用 .m3u8 头文件和 .ts 的 HLS 流视频文件。但系统应该适用于任何类型的文件)。

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

我查看了 0x80004005 的 HResult 与 on msdn 的对应关系,但这只是“E_FAIL 未指定失败”,所以没有运气。

我一直无法弄清楚为什么它会引发套接字关闭的预期(以及为什么它发生在 localhost 部分,但仅在连接时不好)。我确保所有所需的数据都在totalBufferData 中,因此低网速不会影响这一点,因为在我将其写入响应之前所有数据都已下载。我确保我不会在我的代码中的任何地方过早关闭上下文。

到目前为止,我一直未能找到一种方法来访问HttpListener 的底层套接字。我还尝试将response.OutputStream 转换为NetworkStream,并从NetworkStream 获取套接字,但该转换无效(这让我感到困惑,因为它们都是IO 流?)。认为这可能是一个结束的问题,我也尝试过

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

我觉得这个问题与超时有关某处。但是侦听器没有达到默认超时时间。根据to MSDN,所有默认超时应为 2 分钟。我无法接近。而且我认为在超时的情况下返回的异常应该是ObjectDisposedException Write(Byte[], Int32, Int32) was called after the stream was closed.,因为我认为超时会处理连接,而不是崩溃?是误会吗?

请求和响应在单独的线程上完成。但是,在响应仍在进行时完成的请求会排队,并等待响应完成,然后再开始新的响应。

有没有办法获取和调试底层套接字以找出它关闭/崩溃的原因?或者也许是通过 localhost 请求文件并响应不使用 HttpListener 的替代方法?

一些附加信息:它适用于使用脚本运行时版本 .Net 4.x 等效的 Unity 应用程序 (Unity 2019.1),具有 .Net 4.x api 兼容性级别和 IL2CPP 脚本后端。但是处理请求或响应的类都不是从 monobevahiour 继承的(这在统一的线程上甚至是不可能的)。为 Android 构建。


赏金已经结束,但我会为有有价值信息的人开一个新的!

【问题讨论】:

    标签: c# sockets unity3d networking httplistener


    【解决方案1】:

    【讨论】:

    • 不幸的是,PushStreamContent 没有在 Unity 使用的单声道版本中实现,stream 本身也有效,但响应关闭。我认为使用它不会阻止这种情况。
    【解决方案2】:

    这听起来像是我以前遇到过的一个问题,鉴于互联网速度慢或不可靠,我建议可能寻找使用 UDP 套接字而不是 TCP,因为它们不会在连接被短暂切断,或者如果在传输过程中丢失少量数据,请参阅here。 api 非常相似,请参阅here。重新实现可能会有点麻烦,但我认为它会解决您的问题。

    我的另一个见解是,您尝试 catch 块指定它只接受 IOExceptions,即使它正在捕获 SocketException,大多数时候我只是使用通用 Exception 类来避免尝试确定哪些异常将是从哪里扔。

    只是改变:

    catch (IOException e)
    

    catch (Exception e)
    

    IOException 和 SocketException 都继承自 Exception 类,因此其余代码保持不变。希望这可以为您提供更多具体问题的信息。

    【讨论】:

    • 我尝试使用catch (Exception e) 捕获所有异常,但不幸的是,这不会产生任何额外的异常。 UDP 的问题在于它不会阻止 Response 关闭,并且由于 UDP 不会引发异常,因此会使恢复变得更加困难。
    【解决方案3】:

    你可能会遇到一些问题。

    首先是 TimeOut 情况。有可能,因为您在互联网上遇到了一些问题,请求和响应之间的时间比指定的要大(我相信如果您不这样做,它默认设置为 60 秒)。

    另一件事是文件大小可能太大而无法在一个包响应中完整写入。但这会发生在任何请求中,而不仅仅是在“糟糕”的互联网连接时刻。

    也有可能,因为互联网连接不稳定,你的“服务器”检测到“客户端”断开(甚至是短暂的),因此关闭了套接字。

    【讨论】:

      猜你喜欢
      • 2013-05-31
      • 1970-01-01
      • 2010-11-30
      • 2010-09-22
      相关资源
      最近更新 更多