C#NetworkStream.Read奇怪

3Da*_*ave 8 c# asp.net networkstream tcpclient

任何人都能指出这段代码中的缺陷吗?我正在使用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();
}
Run Code Online (Sandbox Code Playgroud)

更新

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

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

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

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

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

感谢大家的出色和详细的回复.

Aar*_*ght 10

相反的是对文档NetworkStream.Read意味着,从获得的数据流TcpClient不能简单的字节数返回0阅读时,有没有可用数据-它块.

如果你查看文档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);
Run Code Online (Sandbox Code Playgroud)

  • @David:这正是HTTP协议具有`content-length`标头的原因.所有浏览器必须做的是读取足够的数据来获取该标头,然后它确切地知道需要读取多少数据.Chunked的工作方式不同,但这超出了这个问题的范围.因此,如果您尝试使用带有HTTP的`TcpClient`(为什么不使用`WebRequest`?),那么唯一可以确定的方法是检查内容长度.如果你不知道有多少数据会回来,你需要依赖`DataAvailable`或者等待一些预定的超时. (3认同)

Tim*_*mbo 2

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