C#中使用HttpListener出现问题

6
我使用了一个 HttpListener 来编写一个处理异步请求的 Windows 服务。
它能正常工作,但有时会遇到需要重新启动服务或服务器才能解决的问题。最初我用以下代码声明了监听器对象:
public HttpListener PointsListener = new HttpListener();

这里是开始监听的方法代码。我从服务的OnStart方法中调用它:

    public string ListenerStart()
    {
        try
        {
            if (!PointsListener.IsListening)
            {
                PointsListener.Prefixes.Add(String.Concat("http://*:", points_port, "/"));
                PointsListener.Start();
                PointsListener.BeginGetContext(PointProcessRequest, PointsListener);

                LogWriter("Http listener activated on port " + points_port);
                return "Listener started";
            }
            else
            {
                return "Listener is already started!";
            }

        }
        catch (Exception err)
        {
            LogWriter("Error in LIstenerStart \r\n" + err.ToString());
            return ("Error: " + err.Message);
        }
    }

这里是处理请求的方法:
    private void PointProcessRequest(IAsyncResult result)
    {
        HttpListener listener = (HttpListener)result.AsyncState;
        HttpListenerContext context = listener.EndGetContext(result);
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;
        response.KeepAlive = false;
        System.IO.Stream output = response.OutputStream;

        try
        {
            //declaring a variable for responce
            string responseString = "<html>My Response: request is not allowed by server protocol</html>";


            // Commands and actions to set responceString

            byte[] buffer = Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            output.Write(buffer, 0, buffer.Length);
        }
        catch (Exception err)
        {
            LogWriter("Error in PointProcessRequest: \r\n" + err.ToString());
        }
        finally
        {
            try
            {
                output.Flush();
                output.Close();
                response.Close();
            }
            catch (Exception err)
            {
                LogWriter("Error in PointProcessRequest CLOSING OUTPUT STREAM: \r\n" + err.ToString());
            }
            finally
            {
                PointsListener.BeginGetContext(PointProcessRequest, PointsListener);
            }
        }
    }

有时候它能正常工作,但是日志中出现了以下错误:

Error in PointProcessRequest: 
System.Net.HttpListenerException: The specified network name is no longer available
   в System.Net.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   в ARK_Dealer.ark.PointProcessRequest(IAsyncResult result)

[26.01.2011 9:00:54] Error in PointProcessRequest CLOSING OUTPUT STREAM: 
System.InvalidOperationException: Cannot close stream until all bytes are written.
   в System.Net.HttpResponseStream.Dispose(Boolean disposing)
   в System.IO.Stream.Close()
   в ARK_Dealer.ark.PointProcessRequest(IAsyncResult result)

我认为问题出现在某个点向服务器发送请求,但在接收答案之前失去连接。

我该如何防止抛出异常?响应对象是否会自动正确处理?我该如何解决这个问题?

4个回答

5

我正在生产环境中使用HttpListener,我发现最好的解决方法是设置Listener.IgnoreWriteExceptions = true;,而不是添加大量无法传达代码逻辑的try/catch块来解决这个问题;这样就可以轻松解决写入异常!


7
这太错误了!如果你不断地向流写入数据,而另一端由于某种原因关闭了连接,你将无法在Write()调用时抛出异常,因为你正在忽略写入异常,并且你的应用程序将继续向流写入数据,对它来说看起来像是打开状态... - Cipi
2
忽略写入异常是正确的方法。我不同意 Cipi 以及他那些在 HTTP 协议下毫无意义的“边缘情况”。 - lubos hasko
3
@Lubos,Cipi的回答很有道理。如果您正在请求下载大文件,那该怎么办呢?如果忽略这些异常,即使字节永远无法到达客户端,您仍将继续读取文件。 - Man Vs Code
最好的方法是捕获错误,然后停止发送更多数据。在事件捕获中,如果您愿意,可以选择忽略它,但是继续向已经关闭的会话发送数据是不好的做法。 - Jean-Claude DuBois

2

最近我也遇到了同样的问题,通过在输出中添加try/catch语句解决:

try
{
    byte[] buffer = Encoding.UTF8.GetBytes(responseString);
    response.ContentLength64 = buffer.Length;
    output.Write(buffer, 0, buffer.Length);
}
catch (HttpListenerException)
{
    // Handle error caused by connection being lost
}

正如您在我的代码中看到的那样,它已经在try catch中了,这就是为什么我可以记录异常并且监听器不会停止...但我正在尝试找到一个解决方案,如果当前连接不再可用,则防止写入输出流。谢谢。 - Khisrav

2
问题解决了!我刚刚移除了这个:
finally
{
PointsListener.BeginGetContext(PointProcessRequest, PointsListener);
}

在PointProcessRequest方法的开头插入此命令!

IAsyncResult _result = listener.BeginGetContext(new AsyncCallback(PointProcessRequest), listener);

问题已经100%解决!

2

有一个相关问题与此相关。

你所做的是正确的,因为对于另一端关闭连接或连接丢失,没有其他可做的事情。你可能需要捕获确切的异常并调用output.Dispose()以及其他清理操作,或者只是让最终处理器处理释放,如果这种情况不经常发生的话。


我稍微修改了PointProcess方法,并像你写的那样插入了Dispose。让我监视系统几天,看看是否会生效。非常感谢! - Khisrav
你好!请评论我的最终决定,当编写响应输出流时,我使用以下代码: using (System.IO.Stream output = response.OutputStream) { output.Write(buffer, 0, buffer.Length); }如果发生异常,它将被写入日志,如果代码运行,则对象输出将自动释放。我是正确的吗?谢谢! - Khisrav
你还在某个地方关闭 response 对象吗? - Amir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接