Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 197 198 199 200 201 202 203 204 205 ... 263
Перейти на страницу:

Формат, приведенный на рис. 27.2, определен в заголовочном файле <netinet/ip_var.h> в виде следующей структуры:

#define MAX_IPOPTLEN 40

struct ipoption {

 struct in_addr ipopt_dst; /* адрес первого получателя */

 char   ipopt_list[MAX_IPOPTLEN]; /* соответствующие параметры */

};

В листинге 27.3 мы анализируем эти данные, не используя указанную структуру.

Возвращаемый формат отличается от того, который был передан функции setsockopt. Если нам было бы нужно преобразовать формат, показанный на рис. 27.2, к формату, показанному на рис. 27.1, нам следовало бы поменять местами первые и вторые 4 байта и изменить значение поля len, добавив к имеющемуся значению 4. К счастью, нам не нужно этого делать, так как Беркли-реализации автоматически используют обращенный маршрут от получателя для сокета TCP. Иными словами, данные, возвращаемые функцией getsockopt (представленные на рис. 27.2), носят чисто информативный характер. Нам не нужно вызывать функцию setsockopt, чтобы указать ядру на необходимость использования данного маршрута для дейтаграмм IP, отсылаемых по соединению TCP, — ядро сделает это само. Подобный пример с нашим сервером TCP мы вскоре увидим.

Следующей из рассматриваемых нами функций, связанных с параметром маршрутизации, полученный маршрут от отправителя передается в формате, показанном на рис. 27.2. Затем она выводит соответствующую информацию. Эту функцию inet_srtcrt_print мы показываем в листинге 27.3.

Листинг 27.3. Функция inet_srtcrt_print: вывод полученного маршрута от отправителя

//ipopts/sourceroute.c

37 void

38 inet_srcrt_print(u_char *ptr, int len)

39 {

40  u_char c;

41  char str[INET_ADDRSTRLEN];

42  struct in_addr hop1;

43  memcpy(&hop1, ptr, sizeof(struct in_addr));

44  ptr += sizeof(struct in_addr);

45  while ((c = *ptr++) == IPOPT_NOP); /* пропуск параметров NOP */

46  if (с == IPOPT_LSRR)

47   printf("received LSRR: ");

48  else if (c == IPOPT_SSRR)

49   printf("received SSRR: ");

50  else {

51   printf("received option type %dn", c);

52   return;

53  }

54  printf("%s ", Inet_ntop(AF_INET, &hop1, str, sizeof(str)));

55  len = *ptr++ - sizeof(struct in_addr); /* вычитаем адрес получателя */

56  ptr++; /* пропуск указателя */

57  while (len > 0) {

58   printf("%s ", Inet_ntop(AF_INET, ptr, str, sizeof(str)));

59   ptr += sizeof(struct in_addr);

60   len -= sizeof(struct in_addr);

61  }

62  printf("n");

63 }

Сохраняем первый адрес IP, пропускаем все параметры NOP

43-45 Первый IP-адрес в буфере сохраняется, а все следующие за ним параметры NOP мы пропускаем.

Проверяем параметр маршрута от отправителя

46-62 Мы выводим информацию о маршруте и проверяем значение поля code, содержащегося в 3-байтовом заголовке, получаем значение поля len и пропускаем указатель ptr. Затем мы выводим все IP-адреса, следующие за 3-байтовым заголовком, кроме IP-адреса получателя.

Пример

Теперь мы модифицируем наш эхо-сервер 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 должны переправлять дейтаграммы и принимать дейтаграммы с маршрутизацией от отправителя, чтобы этот пример работал.

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

1 ... 197 198 199 200 201 202 203 204 205 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии