|
2 лет назад | |
---|---|---|
.. | ||
assets | 2 лет назад | |
README.md | 2 лет назад |
README.md
Использование библиотек
Цель работы
- Уметь устанавливать и подключать к программе внешние библиотеки.
- Уметь использовать типовые элементы API библиотек.
- Уметь работать с параметрами командной строки программы.
Задание
Добавить возможность построения гистограммы по данным из файла в сети. Адрес файла задается аргументом командной строки программы. Если адрес не задан, читать данные со стандартного ввода, как раньше.
Пример запуска программы с загрузкой данных из файла на сайте кафедры:
lab34.exe http://uit.mpei.ru/study/courses/cs/lab03/marks.txt >marks.svg
Работу нужно вести на основе кода общего задания к ЛР № 3 в прежнем репозитарии.
По этой причине во всех примерах используется lab34.exe
.
Защита представляет собой демонстрацию выполненного индивидуального задания, ответы на вопросы по коду и по релевантной теории (см. контрольные вопросы).
Указания
Скачивать файл по сети будем с помощью библиотеки cURL.
Получать сведения о системе будем с помощью API операционной системы. Это тоже внешняя библиотека, но её не требуется устанавливать.
Ввод из произвольного потока
Рефакторинг (refactoring) — это изменение кода программы без изменения её функциональных характеристик (интересующего пользователя поведения).
Зачем менять код, если это не добавляет возможностей и не исправляет ошибок?
Например, для того, чтобы облегчить дальнейшее внесение изменений в него.
В нашем случае программа жестко завязана на то,
что данные поступают со стандартного ввода.
Ожидается, что они смогут поступать не только оттуда.
Код нужно переписать так, чтобы он мог работать с любым входящим потоком.
Тогда текущее поведение будет частным случаем нового,
когда поток — стандартный ввод (cin
).
Что такое «работать с потоком ввода»? Рассмотрим любой пример работы со стандартным потоком ввода:
cin >> number_count;
Здесь есть переменная cin
, которая представляет поток, и операция >>
с ней.
Работать с другим потоком значит использовать иную переменную вместо cin
.
Что такое «работать с любым потоком», если поток — это переменная в программе?
Это значит, в программе есть блок кода для ввода данных,
который принимает переменную-поток и работает с ней.
Но параметризованный блок кода — это же функция!
Вводом данных занимается функция input_data()
.
Следовательно, у нее должен появится параметр — поток, из которого читать.
Input
input_data(istream& in) {
// Прежний код, но работающий с in вместо cin.
}
Это должна быть именно ссылка, потому что нельзя скопировать поток ввода — он объективно один во всей программе. Это не может быть константная ссылка, потому что функция меняет поток: данные после вычитывания оттуда «исчезают».
Вызов функции будет выглядеть так:
auto in = input_data(cin);
Самостоятельно.
Перепишите функцию input_data()
и ее вызов, как предложено.
Убедитесь, что поведение программы не изменилось.
Сделайте коммит.
Самостоятельно.
Добавьте input_data()
новый параметр: bool prompt
.
Если он равен true
, то нужно выводить в cerr
подсказки,
если false
, то не нужно их выводить.
Сделайте коммит.
Загрузка данных по сети
Установка cURL
Страница библиотеки cURL: https://curl.haxx.se/libcurl/.
На этой странице есть раздел Download со ссылкой на страницу загрузок:
Вариантов загрузки очень много, потому что cURL собирается под много операционных систем (Windows, Linux, различные Unix-системы), архитектур процессора (Intel/AMD, ARM и других) и их вариантов (32 или 64 бита). Windows находится в конце списка:
Для Windows следует выбрать ссылку с описанием binary the curl project
:
Странице загрузок для Windows — ссылки, чтобы скачать архив для системы нужной
разрядности. Как выбрать разрядность? Это зависит не от версии Windows,
а от того, собирает компилятор 32-битные или 64-битные программы. По умолчанию
с CodeBlocks идет компилятор 32-битных программ mingw32, он же устанавливается
по инструкции для РПОСУ. Используемый компилятор можно увидеть в Build log
при сборке программы. Если там mingw64, значит, компилятор 64-битный.
В архиве находится папка curl-7.69.1-win32-mingw
(версия cURL и разрядность компилятора та, которая выбрана для загрузки).
Ее нужно извлечь,
переименовать в curl
для удобства
и переместить в каталог проекта.
Самостоятельно.
Занесите эту папку в .gitignore
.
Проверьте, что сделали это правильно, с помощью команды git status
.
Из папки curl
далее понадобятся:
bin/libcurl.dll
— динамическая библиотека (libcurl-x64.dll
для 64 бит);lib
— каталог со статическими библиотеками;include
— каталог с заголовочными файлами.
Примечание. Есть инструменты, которые позволяют скачивать и устанавливать библиотеки из командной строки, для Windows это Chocolatey и vcpkg. Они автоматизируют и стандартизируют то, что в рамках этой ЛР делается вручную, чтобы разобраться.
Подключение cURL к проекту
Каждая программа должна до первого вызова функций библиотеки cURL вызвать
функцию curl_global_init()
.
Например, это можно сделать первой строкой в main()
:
curl_global_init(CURL_GLOBAL_ALL);
При попытке собрать программу возникнут ошибки, текст которых означает,
что константа CURL_GLOBAL_ALL
и функция curl_global_init()
не объявлены,
то есть неизвестны компилятору:
error: 'CURL_GLOBAL_ALL' was not declared in this scope
error: 'curl_global_init' was not declared in this scope
Если открыть документацию по ссылке, она начинается так:
SYNOPSIS
#include <curl/curl.h>
CURLcode curl_global_init(long flags);
«Synopsis» значит «сводка» (отсюда слово «синоптик»), это типовой раздел
справки к библиотекам, в котором кратко показывается, как функция объявлена
и какой заголовочный нужно подключить, чтобы ее использовать. В данном случае
нужно добавить в начало main.cpp
:
#include <curl/curl.h>
При компиляции все равно будет ошибка, но другая — что файл не найден:
fatal error: curl/curl.h: No such file or directory
Как известно из лекций, #include <...>
ищет файл в неких «стандартных
каталогах», куда папка include
из архива не входит. Чтобы добавить её
в список, нужно открыть диалог Project → Build options... и перейти
во вкладку Search directories. Там три дочерних вкладки: Compile
(пути для поиска #include <...>
), Linker (пути для поиска библиотек)
и Resource compiler (не требуется). Нужно нажать кнопку Add, в открывшемся
диалоге указать curl/include
и нажать OK (путь к папке можно также
выбрать через диалог рядом с полем ввода). Каталог curl/include
добавится
в список, диалог можно закрыть кнопкой OK.
Внимание. Рекомендуется вводить пути и имена файлов в настройках как текст, а не выбирать их с помощью диалогов.
Ошибка сборки изменится:
undefined reference to `__imp_curl_global_init'
Обратите внимание, что это уже ошибка не компиляции, а компоновки (линкера).
Исходный текст программы правильный, все заголовочные файлы найдены,
появился obj/Debug/main.o
. Но чтобы создать из него исполняемый файл
не хватает машинного кода функции curl_global_init()
.
Чтобы подключить библиотеку, нужно два действия:
-
Вернуться к Search directories, теперь на дочернюю вкладку Linker. Там нужно добавить каталог
curl/lib
аналогично предыдущему шагу. -
Указать, какую именно библиотеку подключить. Если просмотреть каталог
curl/lib
, там естьlibcurl.a
иlibcurl.dll.a
. Сейчас выберемlibcurl.dll.a
, разница будет пояснена в последующих пунктах. На вкладке Linker settings (соседняя с Search directories) нужно нажать Add и ввестиlibcurl.dll.a
, после чего сохранить настройки и покинуть диалог нажатиями OK.
Если все сделано правильно, сборка должна проходить успешно.
Установка динамических библиотек
Перейдите в каталог bin/Debug
, откройте там консоль и запустите программу:
lab34.exe
Будет ошибка (главное в ней — имя библиотеки libcurl….dll
):
Дело в том, что программа собрана с использованием динамической библиотеки.
В lab34.exe
нет кода cURL, а только указание загрузить libcurl-x64.dll
,
где этот код есть, и вызвать его. Но библиотеки нет в тех каталогах,
где Windows ищет библиотеки. Одним из таких каталогов является текущий.
Скопируйте libcurl-x64.dll
в bin/Debug
и попробуйте запустить программу
(libcurl.dll
для 32 бит).
Самостоятельно. Убедитесь, что программа запускается. Сделайте коммит.
Врезка отсюда до горизонтальной черты не нужна для новых версий cURL.
Если после размещения libcurl….dll
в bin/Debug
программа запускается,
пропустите это дополнение.
Ошибка изменится, теперь не найдены будут libcrypto-1_1.dll
и libssl-1_1.dll
, но их уже нет в поставке cURL. В интернете можно узнать,
что эти библиотеки из набора OpenSSL, нужного cURL для установки защищенных
соединений. Откуда их взять?
Разумеется, в интернете найдется всё:
Никогда не устанавливайте библиотеки из непроверенных источников!
Чтобы установить недостающие библиотеки, нужно посетить официальный сайт, как правило, там есть ссылки для загрузки, и им можно доверять. Кроме того, даже если скачанная библиотека будет подлинной (безопасной), раскладывать отдельные библиотеки вручную ненадежно. Через OpenSSL wiki можно найти сайт с готовыми сборками.
Скачайте установочный пакет (MSI) по ссылке в первой или третьей строке в зависимости от разрядности системы:
Внимание. Разрядность cURL и OpenSSL должны совпадать.
В процессе установки:
- При выборе Copy OpenSSL DLLs to: нужно выбрать The Windows system directory, чтобы библиотеки были доступны при запуске программы из любого каталога.
- На последнем экране автор сборки предлагает поддержать его труд 10 $. Можно просто снять флажок и продолжить. OpenSSL распространяется бесплатно.
Статическая сборка программы
Попробуем собрать программу статически. Чтобы поменять компоновку cURL на статическую, необходимо:
-
Вернуться в Linker settings, выбрать в списке
libcurl.dll.a
, нажать Edit, изменить имя наlibcurl.a
и нажать OK. -
Перейти на вкладку Compiler settings (рядом с Linker settings), выбрать дочернюю вкладку #defines. Там в текстовое поле нужно ввести
CURL_STATICLIB
и выйти из диалога кнопкой OK.
Примечание. Последний шаг специфичен для cURL, аналогичный шаг для других библиотек обычно требуется только на Windows.
При сборке будет множество ошибок:
curl\lib\libcurl.a(connect.o):(.text+0x1d1): undefined reference to `__imp_ntohs'
curl\lib\libcurl.a(connect.o):(.text+0x2ee): undefined reference to `__imp_getpeername'
curl\lib\libcurl.a(connect.o):(.text+0x2f8): undefined reference to `__imp_WSAGetLastError'
...
Причина в том, что код cURL использует функции из других библиотек, поэтому программа должна линковаться с ними всеми. Для этого нужно определять, из какой библиотеки недостающая функция (обычно поиском в интернете), устанавливать эту библиотеку (если она не системная), добавлять её в Linker settings и пробовать собрать снова. В рамках ЛР этого делать не нужно. На практике у зависимостей основной библиотеки могут быть другие лицензии, которые придется принимать во внимание.
Самостоятельно. Удалите все изменения, сделанные в попытках собрать программу статически, при помощи git.
Получение аргументов программы
Из ЛР № 1 известно, что программа может запускаться с аргументами,
которые перечисляются после ее имени через пробел:
fc /N expected.txt actual.txt
.
Здесь fc
(fc.exe
) — исполняемый файл программы,
а /N
, expected.txt
и actual.txt
— три аргумента.
Перенаправление ввода-вывода не является аргументом, например,
команда lab01.exe <input.txt
вызывает lab01.exe
без аргументов.
Получить значения аргументов позволяет следующая версия функции main()
:
int
main(int argc, char* argv[]) {
...
Примечание.
В некоторых источниках можно найти int main(int argc, char** argv)
—
это то же самое.
Все формы void main(...)
являются нестандартными.
Параметр argc
(argument count) показывает количество элементов
в массиве argv
(argument values).
Начальный элемент (с индексом 0) содержит имя программы,
то есть при запуске без параметров argc == 1
.
Самостоятельно.
Напишите в начале main()
код, который в случае, если argc
больше 1,
распечатывает argc
и argv
в формате argv[0] = ...
и выходит (return 0
).
Сделайте коммит. Проверьте на таких примерах, поясните результат:
lab34.exe -x --y /z w
lab34.exe param "with spaces"
lab34.exe param <marks.txt >NUL
(скачайтеmarks.txt
)
Примечание.
Необязательные аргументы,
которые имеют фиксированные имена (например, /N
у fc
),
иногда называют опциями.
Имена опций принято начинать с -
, --
или /
(только в Windows).
Остальные аргументы называют позиционными.
Загрузка файла по сети
Весь код в этом пункте должен выполняться только в случае argc > 1
.
Для простых сценариев cURL имеет набор easy-функций, которые позволяют
скачать файл всего в несколько строк. Первой должна быть вызвана
curl_easy_init()
:
CURL* curl = curl_easy_init();
Что за тип данных CURL*
? Это так называемый дескриптор (handle).
Очень часто библиотека оперирует сложными объектами,
внутреннее устройство которых программе знать не нужно,
но с другой стороны, программа должна сообщать функциям библиотеки,
с каким объектом работать.
В случае cURL таким объектом выступает сетевое соединение и связанные данные.
Единственное, что можно делать с дескрипторами —
передавать их функциям библиотеки и сравнивать между собой.
Переменную типа CURL*
нельзя разыменовать, хотя это и указатель.
Как правило, если есть функция создания дескриптора, как curl_easy_init()
,
есть и функция его освобождения (закрытия).
Действительно, документация гласит:
This call MUST have a corresponding call to curl_easy_cleanup when the operation is complete.
Это означает, что вся работа с curl
должна делаться
между написанной строкой и следующим вызовом:
curl_easy_cleanup(curl);
Самостоятельно.
Изучите пример (раздел EXAMPLE) документации на curl_easy_init()
.
Перенесите пример в программу, но вместо фиксированного адреса
используйте первый аргумент командной строки (с индексом 1).
Убедитесь, что программа работает —
содержимое файла из сети печатается на стандартный вывод.
Код печати argc
и argv
удалите.
Сделайте коммит.
Структура программы должна стать такой:
int
main(int argc, char* argv[]) {
if (argc > 1) {
// работа с cURL
return 0;
}
// прежний код построения гистограммы
}
Обработка ошибок
В переменную res
пример сохраняет результат curl_easy_perform()
.
Значение res
— числовой код ошибки,
а если ошибок не было, CURLE_OK
(равно 0).
Функция curl_easy_strerror()
принимает код ошибки и возвращает строку с текстом ошибки.
Самостоятельно.
Проверяйте результат curl_easy_perform()
, если произошли ошибка,
печатайте её текст и завершайте программу вызовом exit(1)
. Сделайте коммит.
Работа с буфером, загруженным по сети
Содержимое файла из сети нужно не печатать, а разбирать, считывая из него те же данные, что и со стандартного ввода. Проще всего сначала считать весь файл из сети в память. Такое промежуточное хранилище называют буфером. Затем можно организовать поток, который будет читать не из стандартного ввода, а из буфера.
Для загрузки файла выделим функцию download()
,
которая принимает строку с адресом файла.
Файл <sstream>
подключает stringstream
—
поток, который может читать из буфера в памяти.
Также в переменные типа stringstream
можно записывать данные,
то есть заполнять буфер.
#include <sstream>
#include <string>
// ...прежние функции...
Input
download(const string& address) {
stringstream buffer;
// TODO: заполнить буфер.
return input_data(buffer, false);
}
Обратите внимание: CURLOPT_URL
требует адрес в виде строки C (const char*
).
Чтобы получить её из address
типа string
можно как address.c_str()
.
В зависимости от того, переданы ли программе параметры,
она получает исходные данные (Input input
)
либо из сети, либо из стандартного ввода:
int
main(int argc, char* argv[]) {
Input input;
if (argc > 1) {
input = download(argv[1]);
} else {
input = input_data(cin, true);
}
const auto bins = make_histogram(input);
show_histogram_svg(bins);
}
Самостоятельно.
Перенесите код работы с cURL в место, отмеченное комментарием // TODO
.
Убедитесь, что программа компилируется
(запускать ее с параметром пока нельзя).
Сохранение данных из сети в буфер
Как же получить доступ к данным, которые cURL загружает и (пока) печатает? Каждый раз, когда cURL получает из сети очередной блок данных, библиотека может вызывать функцию, которую укажет программа. Эта функция должна добавлять полученный блок данных в буфер.
Такой элемент интерфейса библиотеки называется callback-функцией, или функцией обратного вызова.
Функция должна иметь такой вид:
size_t
write_data(void* items, size_t item_size, size_t item_count, void* ctx) {
// TODO: дописывать данные к буферу.
return 0;
}
Имя функции и параметров может быть любым, но типы данных должны быть строго такими, как описано в документации. Ошибку может быть очень сложно отладить. К счастью, компилятор предупреждает о несоответствии.
Параметры:
void* items
— указатель на принятые данные:item_count
блоков поitem_size
каждый, итогоitem_size * item_count
байтов.size_t item_size
— размер одного блока данных;size_t item_count
— количество блоков данных;void* ctx
— пользовательские данные (поясняется ниже).
Чтобы cURL продолжила загрузку,
функция должна возвращать размер принятых данных в байтах,
иначе curl_easy_perform()
остановит работу с ошибкой.
Пока функция возвращает 0, просто чтобы возвращать какое-нибудь значение.
Указать cURL, какую функцию вызывать, можно так:
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
Самостоятельно.
Внутри write_data()
вычислите количество принятых байтов (см. описание
параметров), сохраните в переменную data_size
.
Далее необходимо внутри write_data()
добавлять данные в буфер, для этого
есть специальный метод buffer.write()
:
buffer.write(items, data_size);
Но это не компилируется: переменная buffer
объявлена в download()
, внутри
write_data()
она недоступна.
Перенести buffer
внутрь write_data()
— не выход:
buffer
нужна и в download()
.
Объявлять в write_data()
еще один буфер тоже неправильно:
это должен быть тот же самый буфер, который затем использует download()
.
Заметим, что хотя buffer
недоступна, она существует где-то в памяти,
потому что write_data()
вызывается из curl_easy_perform()
,
и пока этот вызов не закончен, не закончится и download()
.
Задача в том, чтобы получить этот адрес,
так как имея адрес переменной (то есть указатель на нее), с ней можно работать.
Это типовая необходимость и обычно библиотеки, в том числе cURL,
предоставляют в callback-функциях пользовательские данные (user data).
Перед тем, как позволить библиотеке вызывать функцию,
программа указывает также фиксированные данные (обычно один указатель),
которые будут передаваться библиотекой в функцию.
Именно для этого write_data()
принимает параметр ctx
(контекст).
Можно передавать в этом контексте адрес buffer
так
(перед curl_easy_perform()
):
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
Контекст имеет тип void*
, но нам известно, что это указатель на buffer
,
то есть реальный тип — stringstream*
.
Это как раз тот случай, когда программист может и должен
принудительно указать компилятору тип данных (приведение типов из ЛР № 1):
stringstream* buffer = reinterpret_cast<stringstream*>(ctx);
Поскольку buffer
здесь указатель,
его нужно разыменовать перед вызовом метода:
(*buffer).write(items, data_size);
Осталась последняя ошибка: если прочитать документацию на write()
,
она принимает первым параметром const char*
, а items
имеет тип void*
.
Самостоятельно.
Сделайте приведение типа для items
, чтобы программа компилировалась.
Конструкция (*buffer).write(...)
выглядит громоздко, потому в C++ введен
специальный оператор стрелки, который позволяет вызвать метод по указателю
красивее (параметры write()
прежние, они опущены для краткости):
buffer->write(...);
Самостоятельно. Добейтесь компиляции и проверьте корректную работу программы с примером из задания.
Индивидуальные задания
Всю необходимую информацию по функциям cURL нужно изучить по официальной
документации и на защите предъявить ссылку на конкретную страницу.
В частности, если не указан конкретный параметр curl_easy_setopt()
,
нужно подобрать его по описанию в списке.
Во всех вариантах нужно обрабатывать ошибки cURL, как это делается в основной программе.
Если вариант требует ввода дополнительных данных, проверить работу с файлом
«из сети» можно через локальный файл и адрес вида
file://C:/Users/kozlyuk/Desktop/marks.txt
для файла marks.txt
на рабочем
столе пользователя kozlyuk
. Полный путь к файлу можно получить, щелкнув
в адресной строке «Проводника» (так же, как перед запуском cmd
).
Вариант 1
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
суммарное время (total), затраченное на загрузку файла по сети.
Вариант 2
Добавьте программе опцию -format
, которая определяет формат вывода:
lab34.exe -format text
— текст,
lab34.exe -format svg
— изображение.
Опция может стоять до или после URL:
lab34.exe -format svg http://...
или lab04.exe http://... -format svg
.
Если после -format
не стоит text
или svg
, нужно печатать сообщение
с подсказкой, как запускать программу, и завершать работу.
Вариант 3
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
скорость загрузки файла по сети.
Вариант 4
Добавьте программе опцию -bins
, которая позволяет задать количество
корзин при запуске. Считывать его в read_input()
при этом не нужно.
Например: lab34.exe -bins 3
.
Опция может стоять до или после URL:
lab34.exe -bins 3 http://...
или lab04.exe http://... -bins 3
.
Если после -bins
не стоит числа, нужно печатать сообщение с подсказкой,
как запускать программу, и завершать работу.
Вариант 5
Добавьте программе опцию -generate
, при указании которой программа будет
генерировать числа вместо их считывания. Количество столбцов считывать
по-прежнему нужно. Например, lab34.exe -generate 100
генерирует 100 чисел.
Опция может стоять до или после URL:
lab34.exe -generate 10 http://...
или lab04.exe http://... -generate 10
.
Если после -generate
не стоит числа, нужно печатать сообщение с подсказкой,
как запускать программу, и завершать работу.
Вариант 6
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
размер файла, загружаемого по сети.
Вариант 7
При помощи CURLOPT_PROGRESSFUNCTION
добавьте в программу отображение
прогресса загрузки файла. Каждый раз, когда cURL рапортует прогресс,
печатайте строку вида: Progress: 42%
.
Указание.
Чтобы cURL рапортовала прогресс, нужно установить CURLOPT_NOPROGRESS
в 0.
Вариант 8
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
IP-адрес сервера, с которого скачан файл (не local).
Вариант 9
С помощью функции curl_version_info()
печатайте на стандартный вывод ошибок
версию cURL и версию OpenSSL в строковом виде.
Вариант 10
Добавьте программе опцию -verbose
, при указании которой нужно включать
отладочный вывод cURL с помощью CURLOPT_VERBOSE
.
Опция может стоять до или после URL:
lab34.exe -verbose http://...
или lab04.exe http://... -verbose
.
Если встретился иной аргумент, начинающийся с дефиса, нужно печатать сообщение
с подсказкой, как запускать программу, и завершать работу.
Вариант 11
С помощью функции curl_version_info()
печатайте на стандартный вывод ошибок
список протоколов, которые поддерживает cURL (protocols
).
Вариант 12
Добавьте программе опцию -fill
для задания цвета заливки столбцов. Например,
lab34.exe -fill red
или lab04.exe -fill "#ff0000"
заливает их красным.
Опция может стоять до или после URL:
lab34.exe -fill red http://...
или lab04.exe http://... -fill red
.
Если после -fill
не стоит еще одного аргумента, нужно печатать сообщение
с подсказкой, как запускать программу, и завершать работу.
Вариант 13
Добавьте программе опцию -stroke
для задания цвета границ столбцов.
Например, lab34.exe -stroke red
или lab04.exe -stroke "#ff0000"
делает их
красными.
Опция может стоять до или после URL:
lab34.exe -stroke red http://...
или lab04.exe http://... -fill red
.
Если после -stroke
не стоит еще одного аргумента, нужно печатать сообщение
с подсказкой, как запускать программу, и завершать работу.
Вариант 14
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
размер запроса, который cURL отправила серверу (request size).
Вариант 15
Установите callback-функцию CURLOPT_HEADERDATA
, откуда печатайте
полученные данные на стандартный вывод ошибок.
Вариант 16
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
время, затраченное на установку соединения с сервером (connect).
Вариант 17
Устанавливайте опцию CURLOPT_FAILONERROR
и анализируйте результат
curl_easy_perform()
, чтобы проверить результат загрузки файла,
а не выполнения запроса. Придумайте способ тестирования.
Вариант 18
С помощью функции curl_easy_getinfo()
печатайте на стандартный вывод ошибок
время, затраченное на получение IP-адреса сервера по его имени (name lookup).
Контрольные вопросы
- Сколько аргументов в команде
git add -p file.txt
? Чему равноargc
? - Из каких файлов и как собирается программа, использующая библиотеку?
- Чем отличаются статические и динамические библиотеки?
- Как расшифровывается термин «API» и что представляет собой API библиотеки?
- Что такое дескриптор (handle), для чего он предназначен?
- Что такое функция обратного вызова (callback)?
- Зачем и как применяются пользовательские данные в callback-функциях?