C#NetworkStream.Read奇怪

时间:2010-02-03 17:17:11

标签: c# asp.net tcpclient networkstream

有人能指出这段代码中的缺陷吗?我正在使用TcpClient检索一些HTML。与IIS服务器通信时,NetworkStream.Read()似乎永远不会完成。如果我去使用Fiddler代理,它可以正常工作,但直接与目标服务器通信时,.read()循环将不会退出,直到连接异常时出现“远程服务器已关闭连接”之类的错误。

internal TcpClient Client { get; set; }

/// bunch of other code here...

try
{

NetworkStream ns = Client.GetStream();
StreamWriter sw = new StreamWriter(ns);

sw.Write(request);
sw.Flush();

byte[] buffer = new byte[1024];

int read=0;

try
{
    while ((read = ns.Read(buffer, 0, buffer.Length)) > 0)
    {
        response.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, read));
    }
}
catch //(SocketException se)
{

}
finally
{
    Close();
}

更新

在调试器中,我可以立即看到整个响应并附加到我的StringBuilder(响应)。当服务器完成发送响应或我的代码没有检测到它时,似乎连接没有被关闭。

结论 正如这里所说的,最好利用协议的产品(在HTTP的情况下,Content-Length头)来确定事务何时完成。但是,我发现并非所有页面都设置了内容长度。所以,我现在正在使用混合解决方案:

  1. 对于所有事务,请将请求的Connection标头设置为“关闭”,以防止服务器保持套接字打开。这可以提高服务器在响应您的请求时关闭连接的几率。

  2. 如果设置了Content-Length,请使用它来确定请求何时完成。

  3. 否则,将NetworkStream的RequestTimeout属性设置为一个大但合理的值,例如1秒。然后,循环NetworkStream.Read(),直到a)发生超时,或者b)读取的字节数比你要求的少。

  4. 感谢大家的出色回应。

5 个答案:

答案 0 :(得分:10)

NetworkStream.Read的文档所暗示的相反,从TcpClient获取的流 not 只返回0,表示当没有可用数据时读取的字节数 - 它阻止。

如果查看documentation for TcpClient,您会看到以下这一行:

  

TcpClient类提供了通过网络以同步阻止模式连接,发送和接收流数据的简单方法。

现在我的猜测是,如果您的Read来电被阻止,那是因为服务器决定不再发送任何数据。这可能是因为初始请求没有正确完成。

我的第一个建议是消除StreamWriter作为可能的原因(即缓冲/编码细微差别),并使用NetworkStream.Write方法直接写入流。如果可行,请确保使用StreamWriter的正确参数。

我的第二个建议是不依赖于Read调用的结果来打破循环。 NetworkStream类具有为此设计的DataAvailable属性。编写接收循环的正确方法是:

NetworkStream netStream = client.GetStream();
int read = 0;
byte[] buffer = new byte[1024];
StringBuilder response = new StringBuilder();
do
{
    read = netStream.Read(buffer, 0, buffer.Length);
    response.Append(Encoding.ASCII.GetString(buffer, 0, read));
}
while (netStream.DataAvailable);

答案 1 :(得分:3)

阅读回复,直至达到双重CRLF。你现在拥有的是Response标头。 解析标头以读取Content-Length标头,该标头将是响应中剩余的字节数。

这是一个可以捕获Content-Length标头的正则表达式。

David的更新正则表达式

Content-Length: (?<1>\d+)\r\n

Content-Length

注意

如果服务器未正确设置此标头,我将不会使用它。

答案 2 :(得分:2)

不确定这是否有用但是对于HTTP 1.1,服务器的底层连接可能没有关闭,所以流也可能没有关闭?我们的想法是您可以重用连接来发送新请求。我认为你必须使用内容长度。或者,也可以使用WebClient或WebRequest类。

答案 3 :(得分:1)

我可能错了,但看起来您对Write的呼吁正在(在引擎盖下)写入流ns(通过StreamWriter)。之后,您正在阅读相同的流(ns)。我不太明白你为什么要这样做?

无论如何,您可能需要在流上使用Seek,才能移动到您要开始阅读的位置。我猜它写完后会寻求结束。但正如我所说,我不确定这是否是一个有用的答案!

答案 4 :(得分:0)

两个建议......

  1. 您是否尝试过使用NetworkStream的DataAvailable属性?如果要从流中读取数据,它应该返回true。
  2. 
        while (ns.DataAvailable)
        {
         //Do stuff here
        }
    
    1. 另一种选择是将ReadTimeOut更改为较低的值,这样您就不会长时间阻塞。可以这样做:
    2. 
          ns.ReadTimeOut=100;