UNIX: разработка сетевых приложений
39 }40 }41 exit(0);42 }Эта программа корректно работает на узле с единственным IP-адресом. Если запустить программу из листинга 11.1 на узле с четырьмя IP-адресами, то получим:
freebsd % <b>hostent cnn.com</b>official hostname: cnn.comaddress: 64.236.16.20address: 64.236.16.52address: 64.236.16.84address: 64.236.16.116address: 64 236.24.4address: 64.236.24.12address: 64.236.24.20address: 64.236.24.28Но если запустить программу из листинга Д.4 на том же узле, в выводе будет только первый IP-адрес:
freebsd % <b>hostent2 cnn.com</b>official hostname: cnn.comaddress: 64.236.24.4name = www1.cnn.comПроблема заключается в том, что две функции,
иgethostbyname, совместно используют одну и ту же структуруgethostbyaddr, как было показано в разделе 11.18. Когда наша новая программа вызывает функциюhostent, она повторно использует данную структуру вместе с областью памяти, на которую структура указывает (массив указателейgethostbyaddr), стирая три оставшиеся IP-адреса, возвращаемые функциейh_addr_list.gethostbyname11.2. Если ваша система не поддерживает повторно входимую версию функции
(см. раздел 11.19), то прежде чем вызывать функциюgethostbyaddr, вам следует создать копию массива указателей, возвращаемых функциейgethostbyaddr, и данных, на которые указывает этот массив.gethostbyname11.3. Сервер
отправляет клиенту данные до тех пор, пока клиент не закрывает соединение (то есть пока вы не завершите выполнение клиента).chargen11.4. Эта возможность поддерживается некоторыми распознавателями, но переносимая программа не может использовать ее, потому что POSIX никак ее не оговаривает. В листинге Д.5 приведена измененная версия. Порядок тестирования строки с именем узла имеет значение. Сначала мы вызываем функцию
, поскольку она обеспечивает быстрый тест «внутри памяти» (in-memory) для проверки, является ли строка допустимым IP-адресом в точечно-десятичной записи. Только если тест заканчивается неудачно, мы запускаем функциюinet_pton, которая обычно требует некоторых сетевых ресурсов и времени.gethostbynameЕсли строка является допустимым IP-адресом в точечно-десятичной записи, мы создаем свой массив указателей (
) на один IP-адрес, оставив без изменений цикл, использующийaddrs.pptrПоскольку адрес уже был переведен в двоичное представление в структуре адреса сокета, мы заменяем вызов функции
в листинге 11.2 на вызов функцииmemcpy, так как при вводе IP-адреса в точечно-десятичной записи исходное и конечное поля в данном вызове одинаковые.memmoveЛистинг Д.5. Допускаем как использование IP-адреса в точечно-десятичной записи, так и задание имени узла, номера порта или имени службы
//names/daytimetcpcli2.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int sockfd, n;6 char recvline[MAXLINE + 1];7 struct sockaddr_in servaddr;8 struct in_addr **pptr, *addrs[2];9 struct hostent *hp;10 struct servent *sp;11 if (argc != 3)12 err_quit("usage: daytimetcpcli2 <hostname> <service>");13 bzero(&servaddr, sizeof(servaddr));14 servaddr.sin_family = AF_INET;15 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) == 1) {16 addrs[0] = &servaddr.sin_addr;17 addrs[1] = NULL;18 pptr = &addrs[0];19 } else if ((hp = gethostbyname(argv[1])) != NULL) {20 pptr = (struct in_addr**)hp->h_addr_list;21 } else22 err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));23 if ((n = atoi(argv[2])) > 0)24 servaddr.sin_port = htons(n);25 else if ((sp = getservbyname(argv[2], "tcp")) != NULL)26 servaddr.sin_port = sp->s_port;27 else28 err_quit("getservbyname error for %s", argv[2]);29 for (; *pptr != NULL; pptr++) {30 sockfd = Socket(AF_INET, SOCK_STREAM, 0);31 memmove(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));32 printf("trying %s\n",33 Sock_ntop((SA*)&servaddr, sizeof(servaddr)));34 if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) == 0)35 break; /* успех */36 err_ret("connect error");37 close(sockfd);38 }39 if (*pptr == NULL)40 err_quit("unable to connect");