Ввод / вывод файла C для неизвестных типов файлов: копирование файлов - PullRequest
2 голосов
/ 29 января 2012

возникли проблемы с сетевым заданием. Конечная цель - создать программу на C, которая захватывает файл с заданного URL-адреса через HTTP и записывает его в заданное имя файла. У меня это работает нормально для большинства текстовых файлов, но я сталкиваюсь с некоторыми проблемами, которые, как я подозреваю, происходят из одной и той же основной причины.

Вот быстрая версия кода, который я использую для передачи данных из дескриптора сетевого файла в дескриптор выходного файла:

unsigned long content_length; // extracted from HTTP header
unsigned long successfully_read = 0;
while(successfully_read != content_length)
{
  char buffer[2048];
  int extracted = read(connection,buffer,2048);
  fprintf(output_file,buffer);
  successfully_read += extracted;
}

Как я уже сказал, это прекрасно работает для большинства текстовых файлов (хотя символ% сбивает с толку fprintf, поэтому было бы неплохо иметь способ справиться с этим). Проблема в том, что он просто зависает навсегда, когда я пытаюсь получить нетекстовые файлы (.png - это основной тестовый файл, с которым я работаю, но программа должна уметь обрабатывать что угодно).

Я провел некоторую отладку, и я знаю, что я не перехожу через content_length, не получаю ошибки во время чтения и не сталкиваюсь с узким местом в сети. Я посмотрел онлайн, но весь код ввода / вывода C-файла, который я могу найти для бинарных файлов, кажется, основан на идее, что вы знаете, как структурированы данные внутри файла. Я не знаю, как это устроено, и мне все равно; Я просто хочу скопировать содержимое одного файлового дескриптора в другой.

Может ли кто-нибудь указать мне на некоторые встроенные функции ввода / вывода файлов, которые я могу использовать для этой цели?

Редактировать: Альтернативно, есть ли в заголовке HTTP стандартное поле, которое сообщит мне, как обрабатывать любой файл, с которым я работаю?

Ответы [ 5 ]

4 голосов
/ 29 января 2012

Вы используете не тот инструмент для работы. fprintf принимает строку формата и дополнительные аргументы, например:

fprintf(output_file, "hello %s, today is the %d", cstring, dayoftheweek);

Если вы передадите второй аргумент из неизвестного источника (например, сети, которую вы делаете), вы можете случайно указать %s или %d или другие спецификаторы формата в строке. Затем fprintf попытается прочитать больше аргументов, чем было передано, и вызовет неопределенное поведение.

Используйте fwrite для этого:

fwrite(buffer, 1, extracted, output_file);
1 голос
/ 29 января 2012

Пара вещей с вашим кодом:

Для fprintf - вы используете данные в качестве второго аргумента, когда фактически это должен быть формат, а данные должны быть третьим аргументом.Вот почему у вас возникают проблемы с символом%, и почему он испытывает трудности при представлении с двоичными данными, потому что он ожидает строку формата.выведите файл.

В качестве дополнительного примечания это представляет собой небольшую проблему с безопасностью - если вы извлекаете специально созданный файл с сервера, возможно открывать случайные области вашей памяти.

0 голосов
/ 03 января 2017

Вы открываете файл как текстовый файл.Это означает, что программа будет добавлять \ r \ n символов в конце каждого вызова write ().Попробуйте открыть файл как двоичный файл, и эти ошибки в размере исчезнут.

0 голосов
/ 29 января 2012

Бьюсь об заклад, ваша программа зависает, потому что она ожидает X байтов, но вместо этого получает Y, с X content_length, поэтому ваше условие while(successfully_read != content_length) всегда выполняется.

Вы можете попробовать запустить программу под strace или любым другимэто эквивалентно вашей ОС, если вы хотите увидеть, как ваша программа продолжает пытаться прочитать данные, которые она никогда не получит (потому что вы, вероятно, сделали запрос HTTP / 1.1, который держит соединение открытым, и вы не сделали второйзапрос) или завершился (если сервер закрывает соединение, ваши (повторные) вызовы read (2) просто вернут 0, что оставляет ваше (все еще истинное) условие цикла неизменным.

Если вы отправляетеВывод программы в stdout, вы можете обнаружить, что она не производит вывод - это может произойти, если ресурс, который вы получаете, не содержит символов новой строки или других управляющих символов сброса. Другие режимы буферизации stdio могут применяться, когда вывод идет в файл. (Например,, файл останется пустым, пока буферы stdio не накопят как минимум 4096 байт.)

[*] Thru. Как намекает @ roland-illig, также есть Transfer-Encoding: chunked, что разрушит точную эквивалентность между content_length (предположительно полученным из одноименного заголовка HTTP) и фактическим числом байтов, передаваемых через сокет.

0 голосов
/ 29 января 2012

В дополнение к ответу Сета: если вы не используете стороннюю библиотеку для обработки всего HTTP-содержимого, вам нужно иметь дело с заголовком Transfer-Encoding и возможным сжатием или, по крайней мере, обнаружить их и выдать ошибку, есливы не знаете, как справиться с этим делом.

В целом, это может быть (или не может) быть хорошей идеей для анализа заголовков ответа HTTP, и только если они содержат исключительно то, что вы понимаете,продолжать интерпретировать данные, следующие за заголовком.

...