UNIX: разработка сетевых приложений
Часть 125 из 399 Информация о книге
Листинг 11.2. Наш клиент времени и даты, использующий функции gethostbyname и getservbyname
//names/daytimetcpcli1.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;9 struct in_addr *inetaddrp[2];10 struct in_addr inetaddr;11 struct hostent *hp;12 struct servent *sp;13 if (argc != 3)14 err_quit("usage: daytimetcpcli1 <hostname> <service>");15 if ((hp = gethostbyname(argv[1])) == NULL) {16 if (inet_aton(argv[1], &inetaddr) == 0) {17 err_quit("hostname error for %s: %s", argv[1],18 hstrerror(h_errno));19 } else {20 inetaddrp[0] = &inetaddr;21 inetaddrp[1] = NULL;22 pptr = inetaddrp;23 }24 } else {25 pptr = (struct in_addr**)hp->h_addr_list;26 }27 if ((sp = getservbyname(argv[2], "tcp")) == NULL)28 err_quit("getservbyname error for %s", argv[2]);29 for (; *pptr != NULL; pptr++) {30 sockfd = Socket(AF_INET, SOCK_STREAM, 0);31 bzero(&servaddr, sizeof(servaddr));32 servaddr.sin_family = AF_INET;33 servaddr.sin_port = sp->s_port;34 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));35 printf("trying %s\n", Sock_ntop((SA*)&servaddr, sizeof(servaddr)));36 if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) == 0)37 break; /* успешное завершение */38 err_ret("connect error");39 close(sockfd);40 }41 if (*pptr == NULL)42 err_quit("unable to connect");43 while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {44 recvline[n] = 0; /* null terminate */45 Fputs(recvline, stdout);46 }47 exit(0);48 }Вызов функций gethostbyname и getservbynameПервый аргумент командной строки — это имя узла, передаваемое в качестве аргумента функции13-28, а второй — имя службы, передаваемое в качестве аргумента функцииgethostbyname. Наш код подразумевает использование протокола TCP, что мы указываем во втором аргументе функцииgetservbyname. Если функцииgetservbynameне удается найти нужное имя, мы вызываем функциюgethostbyname(см. раздел 3.6), чтобы проверить, не является ли аргумент командной строки IP-адресом в формате ASCII. В этом случае формируется список из одного элемента — этого IP-адреса.inet_atonПеребор всех адресовТеперь мы пишем вызовы функций29-35иsocketв цикле, который выполняется для каждого адреса сервера, пока попытка вызова функцииconnectне окажется успешной или пока не закончится список серверов. После вызова функцииconnectмы заполняем структуру адреса сокета Интернета IP-адресом и номером порта сервера. Хотя в целях увеличения производительности мы могли бы вынести из цикла вызов функцииsocketи последующие два присваивания, наш код легче читать в таком виде, как он представлен сейчас. Установление соединения с сервером редко является основным источником проблем с производительностью сетевого клиента.bzeroВызов функции connectВызывается функция36-39, и если вызов оказывается успешным, функцияconnectзавершает цикл. Если установить соединение не удается, мы выводим сообщение об ошибке и закрываем сокет. Вспомните, что дескриптор, для которого вызов функцииbreakоказался неудачным, не может больше использоваться и должен быть закрыт.connectЗавершение программыЕсли цикл завершается, потому что ни один вызов функции41-42не закончился успехом, программа завершает работу.connectЧтение ответа сервераМы считываем ответ сервера и завершаем программу, когда сервер закрывает соединение.43-47Если мы запустим эту программу, указав один из наших узлов, на котором работает сервер времени и даты, мы получим ожидаемый результат:
freebsd % <b>daytimetcpcli1 aix daytime</b>trying 192.168.42.2:13Sun Jul 27 22:44:19 2003Но еще интереснее запустить программу, обратившись к маршрутизатору с несколькими сетевыми интерфейсами, на котором не работает сервер времени и даты:
solaris % <b>daytimetcpcli1 gateway.tuc.noao.edu daytime</b>trying 140.252.108.1:13