Литмир - Электронная Библиотека
Содержание  
A
A

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);

Поскольку мы имеем дело с сервером, определяем флаги

NI_NUMERICHOST
и
NI_NUMERICSERV
, чтобы избежать поиска в DNS и
/etc/services
.

11.9. Первая проблема состоит в том, что второй сервер не может связаться (

bind
) с тем же портом, что и первый сервер, поскольку не установлен параметр сокета
SO_REUSEADDR
. Простейший способ справиться с такой ситуацией — создать копию функции
udp_server
, переименовать ее в
udp_server_reuseaddr
, сделать так, чтобы она установила параметр сокета, и вызывать ее в сервере.

390
{"b":"225366","o":1}