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

Пример

Пример

Теперь мы модифицируем наш эхо-сервер TCP таким образом, чтобы выводить полученный маршрут от отправителя, а эхо-клиент TCP — так, чтобы маршрут от отправителя можно было задавать. В листинге 27.4 показан код эхо-клиента TCP.

Листинг 27.4. Эхо-клиент TCP, задающий маршрут от отправителя

//ipopts/tcpcli01.c
 1 #include "unp.h"
 2 int
 3 main(int argc, char **argv)
 4 {
 5  int c, sockfd, len = 0;
 6  u_char *ptr = NULL;
 7  struct addrinfo *ai;
 8  if (argc < 2)
 9   err_quit("usage: tcpcli01 [ -[gG] <hostname> ... ] <hostname>");
10  opterr = 0; /* отключаем запись сообщений getopt() в stderr */
11  while ((с = getopt(argc, argv, "gG")) != -1) {
12   switch (c) {
13   case 'g': /* свободный маршрут от отправителя */
14    if (ptr)
15     err_quit("can't use both -g and -G");
16    ptr = inet_srcrt_init(0);
17    break;
18   case 'G': /* жесткий маршрут от отправителя */
19    if (ptr)
20     err_qint("can't use both -g and -G");
21    ptr = inet_srcrt_init(1);
22    break;
23   case '?':
24    err_quit("unrecognized option: %c", c);
25   }
26  }
27  if (ptr)
28   while (optind < argc-1)
29    len = inet_srcrt_add(argv[optind++]);
30  else if (optind < argc-1)
31   err_quit("need -g or -G to specify route");
32  if (optind != argc-1)
33   err_quit("missing <hostname>");
34  ai = Host_serv(argv[optind], SERV_PORT_STR, AF_INET, SOCK_STREAM);
35  sockfd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
36  if (ptr) {
37   len = inet_srcrt_add(argv[optind]); /* получатель в конце */
38   Setsockopt(sockfd, IPPROTO_IP, IP_OPTIONS, ptr, len);
39   free(ptr);
40  }
41  Connect(sockfd, ai->ai_addr, ai->ai_addrlen);
42  str_cli(stdin, sockfd); /* вызов рабочей функции */
43  exit(0);
44 }

Обработка аргументов командной строки

12-26 Мы вызываем нашу функцию inet_srcrt_init, чтобы инициализировать маршрут от отправителя. Тип маршрутизации указывается при помощи параметра -g (свободная) или -G (жесткая).

27-33 Если указатель ptr установлен, значит, был указан параметр маршрутизации от отправителя, и все указанные промежуточные узлы добавляются к маршруту, подготовленному на предыдущем этапе функцией inet_srcrt_add. Если же ptr не установлен, но в командной строке еще есть аргументы, значит, пользователь задал маршрут, но не указал его тип. В этом случае программа завершает работу с сообщением об ошибке.

Обработка адреса получателя и создание сокета

34-35 Последний аргумент командной строки — это имя узла или адрес сервера в точечно-десятичной записи, который обрабатывается нашей функцией host_serv. Мы не можем вызвать функцию tcp_connect, так как должны задать маршрут от отправителя между вызовом функций socket и connect. Последняя инициирует трехэтапное рукопожатие, а нам нужно, чтобы сегмент SYN отправителя и все последующие пакеты проходили по одному и тому же маршруту.

36-42 Если маршрут от отправителя задан, следует добавить IP-адрес сервера в конец списка адресов (см. рис. 27.1). Функция setsockopt устанавливает маршрут от отправителя для данного сокета. Затем мы вызываем функцию connect, а потом — нашу функцию str_cli (см. листинг 5.4).

Наш TCP-сервер имеет много общего с кодом, показанным в листинге 5.9, но содержит следующие изменения.

Во-первых, мы выделяем место для параметров:

int len;
u_char *opts;
opts = Malloc(44);

Во-вторых, мы получаем параметры IP после вызова функции accept, но перед вызовом функции fork:

len = 44;
Getsockopt(connfd, IPPROTO_IP, IP_OPTIONS, opts, &len);
if (len > 0) {
 printf("received IP options, len = %dn", len);
 inet_srcrt_print(opts, len);
}

Если сегмент SYN, полученный от клиента, не содержит никаких параметров IP, переменная len по завершении функции getsockopt будет иметь нулевое значение (эта переменная относится к типу «значение-результат»). Как уже упоминалось, нам не нужно предпринимать какие-либо шаги для того, чтобы на стороне сервера использовался обращенный маршрут от отправителя: это делается автоматически без нашего участия [128, с. 931]. Вызывая функцию getsockopt, мы просто получаем копию обращенного маршрута от отправителя. Если мы не хотим, чтобы TCP использовал этот маршрут, то после завершения функции accept следует вызвать функцию setsockopt и задать нулевую длину (последний аргумент), тем самым удалив все используемые в текущий момент параметры IP. Но маршрут от отправителя тем не менее уже был использован в процессе трехэтапного рукопожатия при пересылке второго сегмента. Если мы уберем параметры маршрутизации, IP составит и будет использовать для пересылки последующих пакетов какой-либо другой маршрут.

Теперь мы покажем пример клиент-серверного взаимодействия при заданном маршруте от отправителя. Мы запускаем наш клиент на узле freebsd следующим образом:

freebsd4 % tcpcli01 -g macosx freebsd4 macosx

Тем самым дейтаграммы IP отсылаются с узла freebsd на узел macosx, обратно на узел freebsd4, и наконец, на macosx, где запущен наш сервер. Две промежуточные системы freebsd4 и macosx должны переправлять дейтаграммы и принимать дейтаграммы с маршрутизацией от отправителя, чтобы этот пример работал.

Когда соединение устанавливается, на стороне сервера выдается следующий результат:

macosx % tcpserv01
received IP options, len = 16
received LSRR, 172.24.37.94 172.24.37.78 172.24.37.94

Первый выведенный IP-адрес — это первый узел обратного маршрута (freebsd4, как показано на рис. 27.2), а следующие два адреса идут в том порядке, который используется сервером для отправки дейтаграмм назад клиенту. Если мы понаблюдаем за процессом взаимодействия клиента и сервера с помощью программы tcpdump, мы увидим, как используется параметр маршрутизации для каждой дейтаграммы в обоих направлениях.

ПРИМЕЧАНИЕ

К сожалению, действие параметра сокета IP_OPTIONS никогда не было документировано, поэтому вы можете увидеть различные вариации поведения в системах, не происходящих от исходного кода Беркли. Например, в системе Solaris 2.5 первый адрес, возвращаемый функцией getsockopt (см. рис. 27.2) — это не первый адрес в обращенном маршруте, а адрес собеседника. Тем не менее обратный маршрут, используемый TCP, будет корректен. Кроме того, в Solaris 2.5 всем параметрам маршрутизации предшествует четыре параметра NOP, что ограничивает параметр маршрутизации восемью IP-адресами, а не девятью, которые реально могли бы поместиться.

Оглавление книги


Генерация: 0.106. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз