7.6. Вспомогательные функции

Библиотека C во FreeBSD содержит много вспомогательных функций для программирования сокетов. К примеру, в нашем клиенте мы использовали точный IP адрес time.nist.gov. Но IP адрес нам не всегда известен. Даже, если мы знаем его, наша программа будет более гибкой, если мы дадим возможность пользователю вводить IP адрес или имя домена самостоятельно.

7.6.1. gethostbyname

Не существует способа передать имя домена напрямую к какой-либо функции для работы с сокетом. По этой причине C библиотека во FreeBSD содержит функции gethostbyname(3) и gethostbyname2(3), объявленные в файле netdb.h.

struct hostent * gethostbyname(const char *name);
struct hostent * gethostbyname2(const char *name, int af);

Обе функции возвращают указатель на структуру hostent, содержащей достаточно информации о домене. Нам понадобится поле структуры h_addr_list[0], указывающее на h_length байт правильного адреса, уже хранящегося в сетевом порядке байт.

Это позволит нам создать более гибкую, более полезную версию нашей программы daytime:

/*
 * daytime.c
 *
 * Programmed by G. Adam Stanislav
 * 19 June 2001
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
  register int s;
  register int bytes;
  struct sockaddr_in sa;
  struct hostent *he;
  char buf[BUFSIZ+1];
  char *host;

  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return 1;
  }

  bzero(&sa, sizeof sa);

  sa.sin_family = AF_INET;
  sa.sin_port = htons(13);

  host = (argc > 1) ? (char *)argv[1] : "time.nist.gov";

  if ((he = gethostbyname(host)) == NULL) {
    herror(host);
    return 2;
  }

  bcopy(he->h_addr_list[0],&sa.sin_addr, he->h_length);

  if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
    perror("connect");
    return 3;
  }

  while ((bytes = read(s, buf, BUFSIZ)) > 0)
    write(1, buf, bytes);

  close(s);
  return 0;
}

Теперь мы можем передать имя домена (или IP адрес) через командную строку. Программа будет пытаться соединиться с указанным daytime сервером или по умолчанию будет обращаться к time.nist.gov. Даже в последнем случае использование gethostbyname лучше нежели статическое указание 192.43.244.18, т.к. даже при смене IP адреса мы все равно сможем найти этот хост.

Так как получение данных о времени с вашего локального сервера не займёт времени, вы можете запустить daytime дважды: один раз получить время с time.nist.gov, а второй раз со своей системы, а затем узнать насколько точное ваше системное время:

% daytime ; daytime localhost


52080 01-06-20 04:02:33 50 0 0 390.2 UTC(NIST) * 
2001-06-20T04:02:35Z
%

Как вы видите, моя система на две секунды опережала временную зону NIST.

7.6.2. getservbyname

Иногда вы можете быть не уверены в том, какой порт использует определённый сервис. В таких случаях может помочь функция getservbyname(3), также объявленная в netdb.h:

struct servent * getservbyname(const char *name, const char *proto);

Структура servent содержит поле s_port, содержащее соответствующий порт в сетевом порядке байт.

Если мы не знаем какой порт использует сервис daytime, мы можем найти его следующим образом:

  struct servent *se;
  ...
  if ((se = getservbyname("daytime", "tcp")) == NULL {
    fprintf(stderr, "Cannot determine which port to use.\n");
    return 7;
  }
  sa.sin_port = se->s_port;

Всё таки, обычно порт нам известен. Но если вы разрабатываете новый протокол, вы можете тестировать его, используя неофициальный порт. Когда-нибудь вы зарегистрируйте протокол и его порт (он должен появиться в вашем файле /etc/services, куда заглядывает функция getservbyname). Вместо возвращения ошибки в вышеприведённом коде, просто используйте временный номер порта. После того, как протокол появится в /etc/services, ваша программа будет находить её порт без необходимости переписывать код.

Этот, и другие документы, могут быть скачаны с ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

По вопросам, связанным с FreeBSD, прочитайте документацию прежде чем писать в <questions@FreeBSD.org>.
По вопросам, связанным с этой документацией, пишите <doc@FreeBSD.org>.
По вопросам, связанным с русским переводом документации, пишите в рассылку <frdp@FreeBSD.org.ua>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.