【问题标题】:httpWebRequest (The underlying connection was closed: The connection was closed unexpectedly.)httpWebRequest(底层连接已关闭:连接意外关闭。)
【发布时间】:2024-06-21 15:00:01
【问题描述】:

我正在开发一个从网络服务器记录数据的 C# 应用程序。它将以下发布请求发送到网络服务器并等待响应。

    /// <summary>
    /// Function for obtaining testCgi data 
    /// </summary>
    /// <param name="Parameters"></param>
    /// <returns></returns>
    private string HttpmyPost(string Parameters)
    {
        string str = "No response";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriTestCGI);
        request.Method = "POST";

        byte[] bytes = Encoding.UTF8.GetBytes(Parameters);
        request.ContentLength = bytes.Length;

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(bytes, 0, bytes.Length);
        requestStream.Close();

            WebResponse response = request.GetResponse();
            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream);

            try
            {
                var result = reader.ReadToEnd();
            stream.Dispose();
            str = result.ToString();
            reader.Dispose();
        }
        catch (WebException ex)
        {
            //System.Windows.Forms.MessageBox.Show(ex.Message);
            System.Diagnostics.Trace.WriteLine(ex.Message);

        }
        finally
        {
            request.Abort();
        }
        return str;
    }

我收到了错误

> "The underlying connection was closed: The connection was closed
> unexpectedly"

我尝试调试错误,并使用 fiddler 来检查来自 Firefox 的发布请求。令我惊讶的是,每当 Fiddler 是我的程序时,我的程序运行良好。当我关闭提琴手时,我遇到了同样的错误。

我怀疑由于 Fiddler 充当代理,它可能会更改某些设置。 我试过使用 webclient,结果是一样的。

当我尝试在 python 中编写请求时,一切正常,没有问题。当然,我可以选择安装 IronPython 并包装该特定功能,但是我认为这种做法过于矫枉过正且缺乏优雅,因此我正在寻求一种更精简的方法。我怀疑这只是设置调整。

我尝试过修改,在我的情况下它是无动于衷的。

request.Accept 
request.ReadWriteTimeout 
request.Timeout 
request.UserAgent 
request.Headers
request.AutomaticDecompression 
request.Referer
request.AllowAutoRedirect
//request.TransferEncoding 
request.Expect
request.ServicePoint.Expect100Continue 
request.PreAuthenticate 
request.KeepAlive 
request.ProtocolVersion 
request.ContentType

无论是否进行上述调整,代码在 Fiddler 捕获数据时都能正常工作。

另外值得注意的是,程序在

处产生错误
WebResponse response = request.GetResponse();

更新: 按照@EricLaw 的建议,我研究了延迟。 我找到了这篇文章 HttpWebRequest gets slower when adding an Interval 这建议转向 Nagle 算法。 现在没有关闭连接,尽管整体响应有一点延迟(当我使用 winforms,而不是异步时)。

【问题讨论】:

    标签: c# httpwebrequest fiddler connectionexception


    【解决方案1】:

    我在这里写了一点关于 Fiddler 如何“神奇地”解决问题的文章:http://blogs.telerik.com/fiddler/posts/13-02-28/help!-running-fiddler-fixes-my-app-

    您遇到的问题实际上是 .NET Framework 本身的一个错误。 HTTP 的规则是,服务器可以在发送第一个响应后随时关闭 KeepAlive 连接(例如,即使客户端请求 KeepAlive 行为,它也不需要接受连接上的另一个请求)。

    .NET 有一个错误,如果它在响应完成后关闭连接,它预计服务器将包含一个 Connection: close 响应标头。如果服务器在没有 Connection: Close 标头的情况下关闭连接(根据 RFC2616 完全有效),.NET 将在尝试发送连接上的下一个请求时遇到关闭的连接,并将引发此异常。 .NET应该做的是静默创建一个新连接并在该新连接上重新发送请求。

    Fiddler 解决了这个问题,因为它不关心服务器是否关闭连接,它保持与客户端的连接处于活动状态。当客户端发送第二个请求时,Fiddler 尝试重用其与服务器的连接,注意到它已关闭,然后默默地创建一个新连接。

    您可以通过以下方式在代码中缓解此问题:

    1. 对请求禁用 keepalive(这会影响性能)
    2. 捕获异常并自动重试
    3. 更改服务器以使连接保持更长时间

    方法 #3 仅在您控制服务器的情况下有效,并且由于客户端可能位于使用后关闭连接的网关/代理后面,您可能也应该使用方法 #2。

    【讨论】:

    • 我已修改代码如下,将请求的整个主体封装在 try catch 中(并且还添加了一个最大计数器),并且正如您建议的那样,如果我尝试几次最终我会到达期望的结果。 (我还添加了 request.ServicePoint.Expect100Continue = false;)但是成功之前的重试次数似乎是随机的。这正常吗?
    • 不幸的是,我无法使用第 3 项选项。选项 1 似乎确实缓解了这个问题。由于在此应用程序中数据量对我来说是一个问题,您会建议使用不同的东西吗?一个包装的 IronPython 函数 - 获取数据然后照常进行?
    • 您应该在禁用 Keep-Alive 的情况下分析应用程序并检查影响是什么;关键变量是延迟请求数大于实际数据量。
    • @ErikLaw 由于我对 Http 调试比较陌生,您能否建议一些简单的阅读来帮助我入门?
    • 如果您想彻底了解 HTTP,我会选择 RFC2616 和“HTTP:权威参考”,但两者都不是真正的“轻量级”。如果目标只是了解更多关于延迟的信息,只需查看 Fiddler 中的流量并查看“统计信息”选项卡,了解建立连接和发送请​​求需要多长时间。
    【解决方案2】:

    一个建议和一个问题: 1)如果您真的想看看发生了什么,请安装 Wireshark。它将准确地向您显示正在发送/接收的内容。并且可以与提琴手进行比较。 我猜您缺少一个标头,例如 request.ContentType = "...." 但只有 wireshark 会告诉您哪一个(通过您的工作替代发送,而不是通过您的 HttpWebRequest 发送)。

    2) 您是在 http 响应内容中收到错误,还是异常,如果是异常,是在您的 catch 中捕获,还是在请求期间发生,在您的 try 语句之前?

    【讨论】:

    • 我已经安装了 Wireshark 并且我已经确认标题是相同的。 :-(
    【解决方案3】:

    Fiddler 用作 Internet 代理。如果您的代码在 Fiddler 运行时有效(也可能来自浏览器),那么您的代理设置可能有问题。

    【讨论】: