Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

36 err_ret("connect error");

37 close(sockfd);

38 }

39 if (*pptr == NULL)

40 err_quit("unable to connect");

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.c

1 #include "unp.h"

2 int

3 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 } else

30 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
и
sock_set_addr
(см. раздел 3.8), чтобы установить два соответствующих поля в структуре адреса сокета.

Эта программа работает, однако имеется два ограничения. Во-первых, мы должны обрабатывать все различия, следя за

h_addrtype
и задавая соответствующим образом
sa
или
salen
. Более удачным решением было бы иметь библиотечную функцию, которая не только просматривает имя узла и имя службы, но и заполняет всю структуру адреса сокета (например,
getaddrinfo
, см. раздел 11.6). Во-вторых, эта программа компилируется только на узлах с поддержкой IPv6. Чтобы ее можно было откомпилировать на узле, поддерживающем только IPv4, следует добавить в код огромное количество директив
#ifdef
, что, несомненно, усложнит программу.

11.7. Разместите в памяти большой буфер (превышающий по размеру любую структуру адреса сокета) и вызовите функцию

getsockname
. Третий аргумент является аргументом типа «значение-результат», возвращающим фактический размер адресов протоколов. К сожалению, это допускают только структуры адреса сокета с фиксированной длиной (IPv4 и IPv6). Нет гарантии, что этот буфер будет работать с протоколами, которые могут вернуть структуру адреса сокета переменной длины (доменные сокеты Unix, см. главу 15).

11.8. Сначала размещаем в памяти массивы, содержащие имя узла и имя службы:

char host[NI_MAXHOST], serv[NI_MAXSERV];

После того как функция

accept
возвращает управление, вызываем вместо функции
sock_ntop
функцию
getnameinfo
:

if (getnameinfo(cliaddr, len, host, NI_MAXHOST, serv, NI_MAXSERV,

NI_NUMERICHOST | NI_NUMERICSERV) == 0)

printf("connection from %s.%s\n", host, serv);

Поделиться с друзьями: