149: if (argc > 2) {
150: fprintf(stderr, "использование: tftpserver [порт]n");
151: exit(1);
152: }
153:
154: if (argv[1]) portAddress = argv[1];
155:
156: memset(&hints, 0, sizeof (hints));
157:
158: hints.ai_socktype = SOCK_DGRAM;
159: hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
160: if ((rc = getaddrinfo(NULL, portAddress, &hints, &addr)))
161: fprintf(stderr, "сбой поиска порта %sn",
162: portAddress);
163:
164: if ((s = socket(addr->ai_family, addr->ai_socktype,
165: addr->ai_protocol)) < 0)
166: die("socket");
167:
168: if (bind(s, addr->ai_addr, addr->ai_addrlen))
169: die("bind");
170:
171: /* Основной цикл состоит из ожидания tftp-запроса, его обработки
172: и затем ожидания следующего запроса. */
173: while (1) {
174: bytes = recvfrom(s, &packet, sizeof(packet), 0, &from,
175: &fromLen);
176: if (bytes < 0) die("recvfrom");
177:
178: /* Если выполнить разветвление перед вызовом handleRequest() и
179: завершить дочерний процесс после возврата функции, то данный
180: сервер будет работать точно как параллельный tftp-сервер */
181: handleRequest(*addr, from, fromLen, packet);
182: }
183: }
17.7. Ошибки сокетов
Некоторые значения errno встречаются только при работе с сокетами. Ниже приведен список специфических ошибок сокетов вместе с краткими их описаниями.
EADDRINUSE Запрашиваемый адрес уже используется и не может быть переприсвоен. EADDRNOTAVAIL Запрашивается несуществующий адрес. EAFNOSUPPORT Указано неподдерживаемое семейство адресов. ECONNABORTED Соединение прервано программным обеспечением. ECONNREFUSED Удаленная машина отклонила попытку соединения. ECONNRESET Соединение переустановлено удаленным концом. Это, как правило, указывает на то, что удаленная машина была перезагружена. EDESTADDRREQ Выполнена попытка передачи данных через сокет без предоставления адреса назначения. Это может происходить только в дейтаграммных сокетах. EHOSTDOWN Удаленный хост не находится в сети. EHOSTUNREACH Удаленный хост недоступен. EISCONN Для сокета уже установлено соединение. EMSGSIZE Данные, передаваемые через сокет, слишком велики для отправления в одном элементарном сообщении. ENETDOWN Сетевое соединение прекратилось. ENETRESET Сеть была сброшена, что вызвало потерю соединения. ENETUNREACH Указанная сеть недоступна. ENOBUFS Для обработки запроса доступного пространства буфера недостаточно. ENOPROTOOPT Выполнена попытка установить неправильную опцию. ENOTCONN До выполнения операции необходимо установить соединение. ENOTSOCK Специфическая сокетная операция была направлена на файловый дескриптор, который ссылается не на сокет. EPFNOSUPPORT Указано неподдерживаемое семейство протоколов. EPROTONOSUPPORT Запрос был сделан для неподдерживаемого протокола. EPROTOTYPE Для сокета был указан несоответствующий тип протокола. ESOCKTNOSUPPORT Выполнена попытка создания неподдерживаемого типа сокета. ETIMEDOUT Время соединения истекло.
17.8. Унаследованные сетевые функции
В данный момент действует множество библиотечных функций, относящихся к работе сетей TCP/IP, которые нельзя применять в новых приложениях. Однако они широко используются в существующих IPv4-программах. В связи с этим они рассматриваются ниже для того, чтобы помочь вам понять и обновить старые коды.
17.8.1. Манипулирование IPv4-адресами
Функции inet_ntop() и inet_pton() являются относительно новыми и были введены для того, чтобы один набор функций мог обрабатывать и IPv4-, и IPv6-адреса. До их появления в программах использовались функции inet_addr(), inet_aton() и inet_ntoa(), которые предназначены только для IPv4.
Вспомните, что struct sockaddr_in определяется следующим образом
struct sockaddr__in {
short int sin_family; /* AF_INET */
unsigned short int sin_port; /* номер порта */
struct in_addr sin_addr; /* IP-адрес */
}
Член sin_addr представляет собой структуру struct in_addr; унаследованные функции используют его в качестве параметра[145]. Подразумевается, что данная структура является непрозрачной; программы приложений могут обрабатывать struct in_addr исключительно через библиотечные функции. Старой функцией для преобразования IPv4-адреса в десятичную форму с разделительными точками служит inet_ntoa().
#include <netinet/in.h>
#include <arpa/inet.h>
char * inet_ntoa(struct in_addr address);
Передаваемый адрес преобразуется в строку в десятичном формате с разделительными точками, возвращается указатель на данную строку. Строка сохраняется в статическом буфере библиотеки С и уничтожается при следующем вызове inet_ntoa()[146].
Существуют две функции, которые предлагают обратное преобразование десятичной строки в двоичный IP-адрес. Более старая из них функция inet_addr() имеет две проблемы, обе вызванные тем, что она возвращает результат типа long. Она не возвращает struct in_addr, как предполагается остальными стандартными функциями, поэтому программисты были вынуждены выполнять неуклюжие приведения. К тому же, если переменная типа long имела 32 бита, то программы не могли различить возврат числа -1 (что указывает на ошибку, например, неправильный адрес) и двоичного представления адреса 255.255.255.255.
#include <netinet/in.h>
#include <arpa/inet.h>
unsigned long int inet_addr(const char * ddaddress);
Функция принимает передаваемую строку, которая должна содержать десятичный IP-адрес с разделительными точками, и преобразует ее в двоичный IP-адрес.
Для исправления недостатков inet_addr() была введена функция inet_aton().
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char * ddaddress, struct in_addr * address);
Данная функция ожидает строку, содержащую десятичный IP-адрес, и размещает двоичное представление этого адреса в структуре struct in_addr, на которую указывает параметр address. В отличие от большинства библиотечных функций inet_aton() возвращает нуль в случае ошибки и ненулевое значение, если преобразование прошло успешно.
17.8.2. Преобразование имен хостов
Функции getaddrinfo(), getnameinfo(), позволяющие легко создавать программы, которые поддерживают и IPv4, и IPv6, были введены именно с этой целью. Исходные функции имен хостов было сложно расширить на IPv6, их интерфейсы требовали, чтобы приложения учитывали множество особенностей версии в структурах, сохраняющих IP-адрес. Новые интерфейсы абстрактны, поэтому поддерживают IPv4 и IPv6 одинаково.
Вместо того чтобы возвращать связный список, как это делает getaddrinfo(), старые функции имен хостов используют struct hostent, которая может содержать все имена хостов и адреса для одного хоста.
#include <netdb.h>
struct hostent {
char* h_name; /* каноническое имя хоста */
char** h_aliases; /* псевдонимы (завершающиеся NULL) */