Загрузка файла через TcpClient с HTTP-сервера - PullRequest
2 голосов
/ 22 января 2011

Я изучаю 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();
}

Но загруженный файл поврежден. Я не могу понять почему ... Исходный файл

Ответы [ 3 ]

1 голос
/ 24 января 2011
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 читает лишние байты?

0 голосов
/ 23 января 2011

Вам необходимо использовать значение из заголовка Content-Length (которое может быть больше 32-разрядного целого числа, кстати). Продолжайте читать, пока не будет получено столько байтов.

Однако сервер может вообще не использовать заголовок Content-Length. Он может использовать заголовок Transfer-Encoding, который включает кодировку chunked, или он вообще не может использовать заголовок. HTTP определяет несколько различных способов определения длины данных, и вы должны поддерживать столько, сколько можете, чтобы учесть все виды серверов. См. RFC 2616 Разделы 4.3 и 4.4 для конкретных правил, которым вы должны следовать.

0 голосов
/ 23 января 2011

Я предлагаю использовать contentLength вместо networkStream.DataAvailable , чтобы определить, когда передача данных завершена. DataAvailable просто означает, что есть данные, полученные стеком ОС, однако, в зависимости от сетевой маршрутизации и фрагментации, они могут не прийти к моменту проверки состояния, поэтому вы прекращаете получать эти данные слишком рано .

Чтобы доказать или опровергнуть это, вы можете сделать сравнение двоичного файла между тем, что загружала ваша программа, и тем, что загружал бы обычный браузер. Если первый из них усечен, то мы нашли проблему.

...