httpWebRequest(底层连接已关闭:连接意外关闭。)

14

我正在开发一个使用C#编写的应用程序,它可以记录来自Web服务器的数据。它发送以下POST请求到Web服务器并等待响应。

    /// <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发送的POST请求。令我惊讶的是,每当使用Fiddler时,我的程序都能很好地运行。但是当我关闭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添加间隔后变慢 建议关闭Nagle算法。 现在没有关闭的连接,虽然整体响应存在一些延迟(当我使用winforms而不是async时)。

3个回答

31

我稍微介绍一下 Fiddler 如何“神奇地”解决问题:http://blogs.telerik.com/fiddler/posts/13-02-28/help!-running-fiddler-fixes-my-app-

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

.NET 存在一个 bug,它期望服务器在响应完成后包含一个 Connection: close 响应头,如果服务器没有包含 Connection: Close 头(完全符合 RFC2616),.NET 将在尝试在连接上发送下一个请求时遇到关闭的连接,并抛出此异常。.NET 应该做的是静默地创建一个新连接并在新连接上重新发送请求。

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

你可以通过以下方法缓解代码中的问题:

  1. 在请求中禁用 keepalive(这会影响性能)
  2. 捕获异常并自动重试
  3. 将服务器更改为更长时间地保持连接活动状态

第三种方法仅适用于你控制服务器的情况,因为客户端可能在网关/代理后面,关闭使用后的连接,所以你还应该使用第二种方法。


1
我已经修改了代码,将整个请求体封装在try catch中(还添加了一个最大计数器),正如你建议的那样,如果我尝试几次,最终会得到期望的结果。(我还添加了**request.ServicePoint.Expect100Continue = false;**) 然而,在成功之前重试的次数似乎是随机的。这正常吗? - NMech
很遗憾,对我来说选项3不可用。选项1似乎可以缓解问题。 由于数据量在这个应用程序中是一个问题,您是否建议使用其他方法 -例如包装的IronPython函数 - 来获取数据,然后像往常一样继续进行? - NMech
你应该在禁用Keep-Alive的情况下对应用程序进行性能分析,并检查其影响;关键变量是延迟和请求次数,而不是实际数据的数量。 - EricLaw
@ErikLaw 由于我对Http调试相对较新,您能推荐一些简单易懂的读物来帮助我入门吗? - NMech
1
如果你想全面了解HTTP,我会推荐RFC2616和《HTTP权威指南》,但两者都不算“轻松简单”。如果目标只是想更多地了解延迟问题,可以使用Fiddler观察流量,并查看统计选项卡以查看建立连接和发送请求所需的时间。 - EricLaw
显示剩余2条评论

0
一个建议和一个问题: 1)如果你真的想知道发生了什么,请安装Wireshark。它将显示您发送/接收的内容,并使您能够与Fiddler进行比较。 我猜你缺少一个头文件,比如request.ContentType = "....",但只有Wireshark才能告诉你哪个(通过你的工作替代品发送,而不是通过你的HttpWebRequest发送)。
2)你是否在http响应内容中遇到错误,或者是异常?如果是异常,它是否被捕获在你的catch中,还是在请求之前就发生了?

我已经安装了Wireshark,并确认头文件是相同的。 :-( - NMech

-1

Fiddler作为一个互联网代理工具。如果你的代码在Fiddler运行时可以正常工作(也许从浏览器中也可以),那么你可能存在代理设置问题。


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