UNIX: разработка сетевых приложений
Часть 390 из 399 Информация о книге
41 while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {42 recvline[n] = 0; /* завершающий нуль */43 Fputs(recvline, stdout);44 }45 exit(0);46 }11.5. Программа приведена в листинге Д.6.
Листинг Д.6. Модификация листинга 11.2 для работы с IPv4 и IPv6
//names/daytimetcpcli3.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 sockaddr_in6 servaddr6;9 struct sockaddr *sa;10 socklen_t sal en;11 struct in_addr **pptr;12 struct hostent *hp;13 struct servent *sp;14 if (argc != 3)15 err_quit("usage: daytimetcpcli3 <hostname> <service>");16 if ((hp = gethostbyname(argv[1])) == NULL)17 err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));18 if ((sp = getservbyname(argv[2], "tcp")) == NULL)19 err_quit("getservbyname error for %s", argv[2]);20 pptr = (struct in_addr**)hp->h_addr_list;21 for (; *pptr != NULL; pptr++) {22 sockfd = Socket(hp->h_addrtype, SOCK_STREAM, 0);23 if (hp->h_addrtype == AF_INET) {24 sa = (SA*)&servaddr;25 salen = sizeof(servaddr);26 } else if (hp->h_addrtype == AF_INET6) {27 sa = (SA*)&servaddr6;28 salen = sizeof(servaddr6);29 } else30 err_quit("unknown addrtype %d", hp->h_addrtype);31 bzero(sa, salen);32 sa->sa_family = hp->h_addrtype;33 sock_set_port(sa, salen, sp->s_port);34 sock_set_addr(sa, salen, *pptr);35 printf("trying %s\n", Sock_ntop(sa, salen));36 if (connect(sockfd, sa, salen) == 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; /* завершающий нуль */45 Fputs(recvline, stdout);46 }47 exit(0);48 }Используем значение
, возвращаемое функциейh_addrtype, для определения типа адреса. Также используем функцииgethostbynameиsock_set_port(см. раздел 3.8), чтобы установить два соответствующих поля в структуре адреса сокета.sock_set_addrЭта программа работает, однако имеется два ограничения. Во-первых, мы должны обрабатывать все различия, следя за
и задавая соответствующим образомh_addrtypeилиsa. Более удачным решением было бы иметь библиотечную функцию, которая не только просматривает имя узла и имя службы, но и заполняет всю структуру адреса сокета (например,salen, см. раздел 11.6). Во-вторых, эта программа компилируется только на узлах с поддержкой IPv6. Чтобы ее можно было откомпилировать на узле, поддерживающем только IPv4, следует добавить в код огромное количество директивgetaddrinfo, что, несомненно, усложнит программу.#ifdef11.7. Разместите в памяти большой буфер (превышающий по размеру любую структуру адреса сокета) и вызовите функцию
. Третий аргумент является аргументом типа «значение-результат», возвращающим фактический размер адресов протоколов. К сожалению, это допускают только структуры адреса сокета с фиксированной длиной (IPv4 и IPv6). Нет гарантии, что этот буфер будет работать с протоколами, которые могут вернуть структуру адреса сокета переменной длины (доменные сокеты Unix, см. главу 15).getsockname11.8. Сначала размещаем в памяти массивы, содержащие имя узла и имя службы:
char host[NI_MAXHOST], serv[NI_MAXSERV];После того как функция
возвращает управление, вызываем вместо функцииacceptфункциюsock_ntop:getnameinfoif (getnameinfo(cliaddr, len, host, NI_MAXHOST, serv, NI_MAXSERV,NI_NUMERICHOST | NI_NUMERICSERV) == 0)printf("connection from %s.%s\n", host, serv);Поскольку мы имеем дело с сервером, определяем флаги
иNI_NUMERICHOST, чтобы избежать поиска в DNS иNI_NUMERICSERV./etc/services11.9. Первая проблема состоит в том, что второй сервер не может связаться (
) с тем же портом, что и первый сервер, поскольку не установлен параметр сокетаbind. Простейший способ справиться с такой ситуацией — создать копию функцииSO_REUSEADDR, переименовать ее вudp_server, сделать так, чтобы она установила параметр сокета, и вызывать ее в сервере.udp_server_reuseaddr