真的很奇怪在C#中使用TcpClient的HTTP客户端

Isa*_*aac 8 .net webserver tcp telnet httpclient

我正在实现一个简单的HTTP客户端,它只连接到Web服务器并获取其默认主页.在这里,它很好用:

using System;
using System.Net.Sockets;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpClient tc = new TcpClient();
            tc.Connect("www.google.com", 80);

            using (NetworkStream ns = tc.GetStream())
            {
                System.IO.StreamWriter sw = new System.IO.StreamWriter(ns);
                System.IO.StreamReader sr = new System.IO.StreamReader(ns);

                string req = "";
                req += "GET / HTTP/1.0\r\n";
                req += "Host: www.google.com\r\n";
                req += "\r\n";

                sw.Write(req);
                sw.Flush();

                Console.WriteLine("[reading...]");
                Console.WriteLine(sr.ReadToEnd());
            }
            tc.Close();
            Console.WriteLine("[done!]");
            Console.ReadKey();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我从上面的代码中删除下面的行时,程序会在sr.ReadToEnd阻塞.

req += "Host: www.google.com\r\n";
Run Code Online (Sandbox Code Playgroud)

我甚sr.Read替换了sr.ReadToEnd,但它无法读取任何内容.我用Wireshark看看发生了什么:

使用Wireshark http://www.imagechicken.com/uploads/1252514718052893500.jpg捕获的数据包的屏幕截图

如您所见,在我的GET请求之后,Google没有响应并且请求一次又一次地重新传输.我们似乎必须在HTTP请求中指定主机部分.奇怪的部分是我们不.我使用telnet发送此请求并得到了Google的回复.我还捕获了telnet发送的请求,它与我的请求完全相同.

我尝试了很多其他网站(例如雅虎,微软),但结果是一样的.

那么,telnet的延迟是否会导致Web服务器采取不同的行为(因为在telnet中我们实际上是键入字符而不是将它们一起发送到1个数据包中).


另一个奇怪的问题是当我将HTTP/1.0更改为HTTP/1.1时,程序总是在sr.ReadToEnd行上阻塞.我想那是因为Web服务器没有关闭连接.

一种解决方案是使用Read(或ReadLine)和ns.DataAvailable来读取响应.但我不能确定我已阅读所有回复.我如何读取响应并确保HTTP/1.1请求的响应中没有剩余字节?


注意: 正如W3所说,

所述主机请求标头字段必须伴随所有HTTP/1.1 请求

(我为我的HTTP/1.1请求做了这个).但我没有看到HTTP/1.0这样的事情.使用telnet 发送没有Host头的请求也没有任何问题.


更新:

TCP标记中的推送标志已设置为1.我也尝试过netsh winsock reset来重置我的TCP/IP堆栈.测试计算机上没有防火墙也没有防病毒.实际发送数据包是因为另一台计算机上安装的Wireshark可以捕获它.

我也尝试过其他一些请求.例如,

string req = "";
req += "GET / HTTP/1.0\r\n";
req += "s df slkjfd sdf/ s/fd \\sdf/\\\\dsfdsf \r\n";
req += "qwretyuiopasdfghjkl\r\n";
req += "Host: www.google.com\r\n";
req += "\r\n";
Run Code Online (Sandbox Code Playgroud)

在所有类型的请求中,如果我省略Host: part,则Web服务器不响应,如果使用Host: part,即使是无效请求(就像上面的请求一样)也会被响应(通过400:HTTP)错误的请求).

nos主机:他的机器上不需要部件,这使得情况更加奇怪.

Rem*_*eau -2

尝试直接使用 System.Net.WebClient 而不是 System.Net.Sockets.TcpClient:

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            WebClient wc = new WebClient();
            Console.WriteLine("[requesting...]");
            Console.WriteLine(wc.DownloadString("http://www.google.com"));
            Console.WriteLine("[done!]");
            Console.ReadKey();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @isaac - 如果您必须使用 TcpClient,那么您确实需要阅读 http://www.ietf.org/rfc/rfc2616.txt 上的实际 HTTP 规范。您原来的阅读代码在很多情况下都不起作用,因为 ReadToEnd() 是处理它们的错误方法,就像 Aziz 之前所说的那样。 (3认同)