HttpContext.Response底层套接字意外关闭

Rem*_*_rm 6 c# sockets networking httplistener unity-game-engine

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

IAsyncResult result = listener.BeginGetContext(new AsyncCallback(HttpRequestListenerCallback), listener);
result.AsyncWaitHandle.WaitOne(1);
Run Code Online (Sandbox Code Playgroud)

当发出请求时,回调会HttpListenerContext为所请求的文件(仅限该文件)创建一个,并提取如下文件名:

HttpListenerContext context = listener.EndGetContext(result);
string fileName = context.Request.Url.AbsolutePath.Substring(1);
Run Code Online (Sandbox Code Playgroud)

将上下文添加到调用httpContexts并链接到int 的字典中commandSequenceNumber以跟踪请求.

如果文件名有效,则会将请求发送到服务器以下载文件.文件被下载并被放入一个名为的字节数组中totalBufferdata.到这里一切都很完美.

现在我想将请求的(视频)文件的字节数据写回请求文件HttpListenerContext.Response的上下文的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
Run Code Online (Sandbox Code Playgroud)

这会通过请求的上下文(即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查询了msdn上被证实的HResult ,但这只是"E_FAIL未指定的失败",所以没有运气.

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

到目前为止,我一直没有找到一种方法来获得底层套接字HttpListener.我也试过转换response.OutputStream为a NetworkStream,并从NetworkStream获取套接字,但该转换无效(这使我感到困惑,因为它们都是IO流?).认为这可能是一个关闭问题我也尝试过

using(Stream testStream = response.OutputStream)
{
    testStream.Write(totalBufferdata.ToArray(), 0, totalBufferdata.Count);
}
Run Code Online (Sandbox Code Playgroud)

我觉得这个问题与某个地方的超时有关.但是监听器没有达到默认的超时时间.根据MSDN,所有默认超时应为2分钟.我在大约30秒到一分钟后得到错误.并且我认为在超时的情况下返回的异常应该是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构建.


赏金已经结束,但我仍然会给那些有价值信息的人提供100分!

小智 -1

你可能在那里遇到一些问题。

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

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

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