从HTTP服务器通过TcpClient下载文件

时间:2011-01-22 14:44:02

标签: http tcpclient downloading

我正在学习HTTP,并决定使用TcpClient从互联网上下载文件。

private void requireBtn_Click(object sender, EventArgs e) {
  var client = new TcpClient(addressBox.Text, 80);
  var networkStream = client.GetStream();

  var sWriter = new StreamWriter(networkStream, Encoding.Default);
  foreach (string querLine in queryBox.Lines) {
    sWriter.WriteLine(querLine);
  }
  sWriter.WriteLine();
  sWriter.Flush();

  string responceText = "";

  var sReader = new StreamReader(networkStream);
  string respLine;
  while ((respLine = sReader.ReadLine()).Length > 0) {
    responceText += respLine + "\r\n";
  }

  responceBox.Text = responceText;

  Regex reContentLength = new Regex(@"(?<=Content-Length:\s)\d+", RegexOptions.IgnoreCase);
  Int32 contentLength = Int32.Parse(reContentLength.Match(responceText).Value);
  this.Text = contentLength.ToString();

  if (networkStream.CanRead) {
    var fileStream = new FileStream(@"C:\img.png", FileMode.Create);

    byte[] buffer = new byte[1024];

    int numberOfBytesRead = 0;
    do {
      numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
      fileStream.Write(buffer, 0, numberOfBytesRead);
    }
    while (networkStream.DataAvailable);

    fileStream.Flush();
    fileStream.Close();

    MessageBox.Show("Done!");
  }
  else
    MessageBox.Show("Fail!");  

  client.Close();
}

但下载的文件已损坏。我不明白为什么...... Source file

3 个答案:

答案 0 :(得分:1)

private void requireBtn_Click(object sender, EventArgs e) {
  string host = "atomAltera.SkyHost.ge";
  string query = "GET /mems.txt HTTP/1.1\r\n" +
                 "Host: atomAltera.SkyHost.ge\r\n" +
                 "User-Agent: NuclightWeb\r\n" +
                 "Connection: close\r\n"+
                 "\r\n";

  var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
  client.Connect(host, 80);   

  var networkStream = new NetworkStream(client);

  var bytes = Encoding.Default.GetBytes(query);
  networkStream.Write(bytes, 0, bytes.Length);


  var bReader = new BinaryReader(networkStream, Encoding.Default);

  string responce = "";
  string line;
  char c;

  do {
    line = "";
    c = '\u0000';
    while (true) {
      c = bReader.ReadChar();
      if (c == '\r')
        break;
      line += c;
    }
    c = bReader.ReadChar();
    responce += line + "\r\n";
  } while (line.Length > 0);  

  responceBox.Text = responce;

  Regex reContentLength = new Regex(@"(?<=Content-Length:\s)\d+", RegexOptions.IgnoreCase);
  Int32 contentLength = Int32.Parse(reContentLength.Match(responce).Value);

  this.Text = contentLength.ToString();

  var fileStream = new FileStream(@"C:\m.txt", FileMode.Create);

  byte[] buffer = new byte[4 * 1024];
  int n = 0;
  int read = 0;

  while (n < contentLength) {
    if (networkStream.DataAvailable) {
      read = networkStream.Read(buffer, 0, buffer.Length);
      n += read;
      fileStream.Write(buffer, 0, read);
    }
  }

  fileStream.Flush();
  fileStream.Close();


  client.Close();
}

有效!可能是StreamReader读取多余的字节?

答案 1 :(得分:0)

我建议使用 contentLength 而不是 networkStream.DataAvailable 来检测数据传输何时完成。 DataAvailable 只是意味着操作系统堆栈接收到数据,但是根据您检查条件时可能没有到达的网络路由和碎片,所以您也停止接收该数据早。

为了证明或反驳这一点,您可以在程序下载的内容与普通浏览器下载的内容之间进行二进制文件比较。如果前者被截断,那么我们就找到了问题。

答案 2 :(得分:0)

您需要使用Content-Length标头中的值(可以大于32位整数,顺便说一句)。继续阅读,直到收到很多字节为止。

但是,服务器可能根本不使用Content-Length标头。它可以使用包含Transfer-Encoding编码的chunked标头,也可以根本不使用任何标头。 HTTP定义了几种确定数据长度的不同方法,您应该尽可能多地支持以便考虑所有类型的服务器。有关您需要遵循的特定规则,请参阅RFC 2616第4.3和4.4节。