代码之家  ›  专栏  ›  技术社区  ›  3Dave

C networkstream.read odty

  •  8
  • 3Dave  · 技术社区  · 14 年前

    有人能指出这段代码的缺陷吗?我正在用tcpclient检索一些HTML。networkstream.read()在与IIS服务器对话时似乎从未完成。如果我改为使用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,是内容长度头)来确定事务何时完成。但是,我发现并不是所有页面都设置了内容长度。所以,我现在使用的是混合解决方案:

    1. 对于所有事务,设置请求的 Connection 头改为“关闭”,这样服务器就不鼓励保持套接字打开。这提高了当服务器通过响应您的请求而关闭连接的机会。

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

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

    感谢所有人的出色和详细的回答。

    5 回复  |  直到 14 年前
        1
  •  2
  •   Timbo    14 年前

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

        2
  •  10
  •   Aaronaught    14 年前

    与文件的目的相反 NetworkStream.Read 意味着,从 TcpClient 如果没有可用的数据,只需返回0作为读取的字节数-它会阻塞。

    如果你看 documentation for TcpClient ,您将看到这一行:

    tcpclient类提供了通过网络连接、发送和接收流数据的简单方法 同步闭锁方式 .

    现在我想如果你 Read 调用被阻塞,这是因为服务器决定不发送任何数据。这可能是因为初始请求未正确通过。

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

    我的第二个建议是不要依赖于 调用以中断循环。这个 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);
    
        3
  •  3
  •   3Dave    14 年前

    读取响应直到达到双CRLF。您现在拥有的是响应头。 解析头以读取Content-Length头,该头将是响应中剩余的字节数。

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

    大卫更新了regex

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

    Content-Length

    注释

    如果服务器没有正确设置这个头,我就不会使用它。

        4
  •  1
  •   Tomas Petricek    14 年前

    我可能错了,但你打电话给 Write 正在(在引擎盖下)往小溪里写 ns (通过 StreamWriter )稍后,您将从同一个流中阅读( 纳秒 )我不太明白你为什么这么做?

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

        5
  •  0
  •   thorkia    14 年前

    两个建议…

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