在使用HttpWebRequest时出现“底层连接已关闭”的问题

9
我有一个用VB.NET编写的应用程序(不是 asp.net,而是Windows Console应用程序)。 我试图调用一个url(一个html页面),并将响应返回到一个字符串中。 响应是纯JSON,没有任何HTML标记。 它以{开头,并以}结尾。
我成功创建了HttpWebRequest对象。 然后调用req.GetResponse()。 一旦我这样做,我就会收到错误消息The underlying connection was closed: The connection was closed unexpectedly. 我一直在谷歌和查看stackoverflow,并尝试了所有适用的方法(很多与WCF服务配置有关,但这些不适用)。
以下是我的代码:
Public Function GetJSON(ByRef d As db.Device) As Boolean
    Try
        d.Url = "http://" & d.IpAddress & ini.doc.<svc>.<url>.Value

        Dim req As HttpWebRequest = HttpWebRequest.Create(d.Url)
        // req.Accept = "*/*"
        // req.Timeout = 30000
        // req.ReadWriteTimeout = 30000
        // req.KeepAlive = False
        // req.UseDefaultCredentials = True
        // req.CachePolicy = HttpWebRequest.DefaultCachePolicy
        // req.Proxy = HttpWebRequest.DefaultWebProxy
        // req.ProtocolVersion = New System.Version(1, 0)

        Dim rsp As HttpWebResponse = req.GetResponse()

        Return True
    Catch ex As Exception
        Log(ex.Message)
        Return False
    Finally
        rsp = Nothing
        req = Nothing
    End Try
End Function

被注释掉的行(注释风格不正确,但 SO 可以正确解析)是我根据网上找到的信息尝试过的所有方法。没有一个能解决它。我已经验证了正在构建的 URL 是正确的;如果我在浏览器中调用完全相同的 URL,它会返回完全正确的预期响应。
我尝试了 wiresharking 它... 我在 shark 输出中看到实际的预期完整数据返回,然后是几行,然后是一条红线,上面写着: http > 51943 [RST] Seq=1607 Win=0 Len=0 这是在 .NET 抛出错误之前显示的最后一行。
我还尝试打开 System.Net 跟踪/日志记录,按照 SO 上的帖子,在输出文件中我看到类似地所有预期的 JSON 数据都会返回,但在返回后,它会将这些行抛出到 .NET 跟踪日志中:
System.Net.Sockets Verbose: 0 : [7040] Exiting Socket#60467532::Receive()   -> 1605#1605
System.Net.Sockets Verbose: 0 : [7040] Socket#60467532::Receive()
System.Net.Sockets Verbose: 0 : [7040] Data from Socket#60467532::Receive
System.Net.Sockets Verbose: 0 : [7040] 00000000 :                                                 : 
System.Net.Sockets Verbose: 0 : [7040] Exiting Socket#60467532::Receive()   -> 0#0
System.Net.Sockets Verbose: 0 : [7040] Socket#60467532::Dispose()
System.Net Error: 0 : [7040] Exception in the HttpWebRequest#27806816:: - The underlying connection was closed: The connection was closed unexpectedly.
System.Net Error: 0 : [7040] Exception in the HttpWebRequest#27806816::GetResponse - The underlying connection was closed: The connection was closed unexpectedly.

有什么想法可以尝试解决这个问题吗?我们正在读取一些环境监测传感器设备的数据,并且他们给了我们这个URL来使用。
其中两件事让我困惑:
a)在浏览器中调用时,它完全正常运行
b)WireShark和.NET跟踪都显示所有数据实际上确实已经返回,但由于某种原因,框架在接收到所有数据后才出现问题!
WebException本身几乎没有任何用处,因为它的InnerException为空,而其状态只是说“ConnectionClosed {8}”。
提前致谢!!!
更新08/18 1130:我现在也尝试过仅使用System.Net.WebRequest而不是HttpWebRequest。这也没有任何区别。
更新08/18 1222:我刚刚尝试将我的代码从使用[Http]Web [Request | Response]切换为调用WebClient对象并使用其DownloadString()方法。然而,这也会抛出完全相同的错误。
更新08/18 1230:尝试使用My.Computer.Network.DownloadFile() - 也会出现相同的连接关闭错误。

这是我的完整network.log .NET跟踪文件的pastebin链接:http://pastebin.com/QfY05a11。您可以在第41行看到所有返回的JSON数据。 - eidylon
这是Wireshark流量日志的pastebin链接:http://pastebin.com/0hbEQerc。您可以在第607行看到返回的数据。 - eidylon
6个回答

5
您能在pastebin.com上发布完整的跟踪日志内容并在此处发布链接吗?
您可能会遇到此异常,因为服务器在“Content-Length”头中说它正在发送N个实体字节,但实际上发送的字节数少于N个,并关闭连接。
回答:
感谢提供数据。从跟踪日志和wireshark跟踪结果来看,似乎服务器没有发送任何响应头,直接发送数据。这是HTTP协议的违规行为。这就是客户端抛出异常的原因。

还有一个bug在你的代码中。你没有处理从调用GetResponse()方法获取到的HttpWebResponse 对象。你需要调用Dispose()方法处理该对象,否则服务器连接将被耗尽。 - feroze
谢谢提供信息。我明天得在办公室修复代码。不过我有一个问题(我猜明天也能找到答案)...如果只切换到 System.Net.WebRequest,理论上应该不需要查找HTTP头,这样做是否可行? - eidylon
你好,感谢迄今为止的帮助。我尝试使用普通的 WebRequest 对象而不是 HttpWebRequest,但这并没有帮助。一定有一些方法可以告诉它接受这个请求,也许通过添加头或其他东西到我的请求?我还尝试放置代码以处理我的响应对象,但它实际上没有 Dispose() 方法,所以在 finally 块中,我只需将 rsp 和 req 都设置为 Nothing。 - eidylon
2
此外,如果您无法修复服务器,则建议直接使用Socket/TcpClient并读取响应JSON。 - feroze
1
@feroze - 关于 HttpWebResponse 对象的处理:它可以被处理吗?Intellisense 没有提供 .Dispose() 方法。 - JYelton
显示剩余4条评论

4

这对我很有帮助,希望也能帮到其他人。

在创建HttpWebRequest对象后,添加以下代码:

System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;

它的作用是指定安全套接字层(SSL)3.0作为安全协议。

http://support.microsoft.com/kb/915599


3

以下是我是如何让它正常工作的...感谢 feroze 指引我正确的方向!(奖励积分)

Public Function GetJSON(ByRef d As db.Device) As Boolean
    Try
        Dim tcp = New TcpClient()
        tcp.Connect(d.IpAddress, 80)

        Dim ns = tcp.GetStream()

        Dim req As Byte() = System.Text.Encoding.ASCII.GetBytes(
            "GET /getdata.htm HTTP/1.1" & vbCrLf & vbCrLf
        )
        ns.Write(req, 0, req.Length)

        Dim rsp(2048) As Byte, rcv As Integer
        Do
            rcv = ns.Read(rsp, 0, rsp.Length)
            d.JSON &= System.Text.Encoding.ASCII.GetString(rsp, 0, rcv)
        Loop Until rcv = 0

        tcp.Close()

        Return True
    Catch ex As Exception
        Log(ex.Message)
        Return False
    End Try
End Function

1
tcp.Close() 应该在 "finally" 中调用,否则如果在 .Close() 调用之前出现异常,则不会被调用。 - sharptooth

0

检查请求的延迟。如果超过几毫秒,则值得禁用Nagle算法

 ServicePointManager.UseNagleAlgorithm = false;

0
我遇到了一个关于没有请求主体的问题。在我的情况下,将ContentLength设置为零解决了这个问题。

0
在我的情况下,问题是我们使用了“http://..”作为服务地址,而不是“https://”。

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