Системные вызовы для работы с сетью, отдают ядру ОС всю часть грязной работы, давая нам только высокоуровневые операции для работы.
Самое важное, при работе с системными вызовами - это порядок вызова и обработка ошибок.
Так как работа сети немыслима без внутренних функций ядра ОС, мы вынуждены использовать системные вызовы для работы с сетью.
В этой статье мы разберем порядок системных вызовов и сигнаруты их функций. Для краткости, обработка ошибок делаться не будет. Однако, при работе с системными вызовами, обработка ошибок - обязательна.
Большинство системных вызовов возращают определенное целочисленное значение, сигнализирующее об успешности иили сбое, например, при сболе - -1 (но не все!). Далее, нужно обработать ситуацию ошибки, обернув ее в if:
int syscall_res = some_sys_call(); if ( syscall_res == -1 ) { // ваша обработка ошибок. perror("some_sys_call failed."); exit(1); }
Получить сообщение об конкретной ошибке, можно с помощью переменной errno, или вывести ее простым вызовом perror() из <stdio.h>.
Эта функция одна из самых необходимых, она позволяет настроить структуры, которыми вы будете пользоваться в дальнейшем. Имеет множество опций, но ее использование, на самом деле, довольно простое.
getaddrinfo() делает следующее:
- избавляет вас, от конкретной версии IP-протокола (IPv4 или IPv6), работает с обеими, автоматически.
- может проводить DNS-поиск имени хоста (если вместо адреса, передадите имя хоста, например
example.com) - заполняет необходимые структуры
int getaddrinfo(
const char* node, // 'example.com' или IP-адрес
const char* service, // например, http, или номер порта
const struct addrinfo* hints,
struct addrinfo** res
);Вы даете этой функции три вводных параметра, а она вам возвращает связный список результатов res.
Параметры функции:
-
const char* node- это может быть имя хостаexample.com, или IP-адрес41.152.322.22. -
const char* service- можно передать номер порта"8080"или ключ конкретной службы -http,ftp,telnet, что угодно. -
const struct addrinfo* hints- заранее заполненная информация с настройками.
Простой пример использования getaddrinfo(), если вы хотите сделать сервер, который прослушивает на вашем IP-адресе и порте 3490:
int status;
struct addrinfo hints;
struct addrinfo* servinfo; // результаты getaddrinfo()
memset(&hints, 0, sizeof(hints)); // обнуление структуры параметров (обязательно)
// заполнение параметров
hints.ai_family = AF_UNSPEC; // без разницы IPv4 или IPv6
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_PASSIVE; // заполнение моего IP за меня
if( ( satus = getaddrinfo( NULL,"3490",&hints,&servinfo ) ) != 0 ) {
fprintf(stderr, "gai error: %s\n", gai_strerror(status));
exit(1);
}
// servinfo сейчас указывает на связный список struct addrinfo размером 1 или более
//... делается что угодно, пока требуется servinfo
freeaddrinfo(servinfo); // освобождение связного спискаДля получения строки ошибки из функции
getaddrinfo()используется специальная функцияgai_strerror(status), кудае передается возвращаемое значение изgetaddrinfo().
Связный список struct addrinfo* servinfo далее можно использовать: для перебора его элементов, получения из кажого этого элемента struct sockaddr для наших нужд.
А вот пример использования getaddrinfo() со стороны клиента, который хочет подключиться к хосту www.example.net на порте 3490:
int status;
struct addrinfo hints;
struct addrinfo *servinfo;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);
// и так далее.show_ip.c - ссылка.
Эта программа получает аргументом командной строки имя хоста, и выводит все его IP-адреса в терминал. В ней используется функция getaddrinfo(), которая создает связный список адресов хоста.
Пример вывода:
$ ./show-ip yandex.ru
IP addresses for yandex.ru:
IPv4: 5.255.255.77
IPv4: 77.88.55.88
IPv4: 77.88.44.55
IPv6: 2a02:6b8:a::a
./show-ip goodgame.ru
IP addresses for goodgame.ru:
IPv4: 127.0.0.1Теперь, имея результат работы функции getaddrinfo(), а именно связный список, правильно настроенных адресов, мы можем приступить к дальнейшим системным вызовам для сетевых операций, которым нужны эти адреса.