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 = %d\n", len);

 inet_srcrt_print(opts, len);

}

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

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

freebsd4 % <b>tcpcli01 -g macosx freebsd4 macosx</b>

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


Перейти на страницу:
Изменить размер шрифта: