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

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

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

21-23 Клиент блокируется и ожидает получения эхо-ответа сервера.

Отображение полученного эхо-ответа

24-26 Клиент выводит на экран полученное от сервера сообщение, вместе с номером потока и последовательным номером сообщения в этом потоке. После этого клиент возвращается на начало цикла, ожидая, что пользователь введет следующую строку.

Запуск программы

Мы запустили эхо-сервер SCTP без аргументов командной строки на компьютере, работающем под управлением FreeBSD. Клиенту при запуске необходимо указать IP-адрес сервера.

freebsd4% sctpclient01 10.1.1.5

[0]Hello                                     Отправка сообщения по потоку 0

From str:1 seq:0 (assoc:0xc99e15a0):[0]Hello Эхо-ответ сервера в потоке 1

[4]Message two                               Отправка сообщения по потоку 4

From str:5 seq:0 (assoc.0xc99e15a0):[4]Message two   Эхо-ответ сервера

                                                     в потоке 5

[4]Message three                             Отправка сообщения по потоку 4

From str:5 seq:1 (assoc 0xc99e15a0):[4]Message three Эхо-ответ сервера

                                                     в потоке 5

^D                                           Ввод символа EOF

freebsd4%

Обратите внимание, что клиент отправляет сообщения по потокам 0 и 4, а сервер отвечает ему по потокам 1 и 5. Именно такое поведение и ожидается в том случае, когда наш сервер запускается без аргументов командной строки. Заметьте также, что порядковый номер сообщения по пятому потоку увеличился на единицу при приеме третьего сообщения, как и должно было произойти.

10.5. Блокирование очереди

Наш сервер позволяет отправлять текстовые сообщения по любому из нескольких потоков. Поток SCTP — это вовсе не поток байтов, как в TCP. Это последовательность сообщений, упорядоченных в пределах ассоциации. Потоки с собственным порядком используются для того, чтобы обойти блокирование очереди (head-of-line blocking), которое может возникать в TCP.

Блокирование возникает при потере сегмента TCP при передаче и приходе следующего за ним сегмента, который удерживается до тех пор, пока утраченный сегмент не будет передан повторно и получен адресатом. Задержка доставки последующих сегментов гарантирует, что приложение получит данные в том порядке, в котором они были отправлены. Это совершенно необходимая функция, которая, к сожалению, обладает определенными недостатками. Представьте, что семантически независимые сообщения передаются по одному соединению TCP. Например, веб-сервер может передать браузеру три картинки для отображения на экране. Чтобы картинки выводились на экран одновременно, сервер передает сначала часть первого изображения, затем часть второго и часть третьего. Процесс повторяется до тех пор, пока все три картинки не будут переданы клиенту целиком. Что произойдет, если потеряется сегмент TCP, относящийся к первому изображению? Клиент не получит никаких данных до тех пор, пока недостающий сегмент не будет передан повторно и доставлен ему. Задержаны будут все три изображения, хотя сегмент относился только к одному из них (первому). Эту ситуацию иллюстрирует рис. 10.2.

Рис. 10.2. Отправка трех изображений по одному TCP-соединению


ПРИМЕЧАНИЕ

Хотя HTTP работает иначе, были предложены расширения этого протокола, такие как SCP [108] и SMUX [33], которые обеспечивают описанную функциональность поверх TCP. Эти протоколы мультиплексирования позволяют избежать проблем, связанных с параллельными TCP-соединениями, не имеющими общей информации о состоянии [123]. Несмотря на то что создание одного TCP-соединения для каждого изображения (как обычно и делают клиенты HTTP) позволяет избежать блокирования, каждому соединению приходится тратить время на определение времени обращения и доступной пропускной способности. Потеря сегмента, относящегося к одному соединению (признак затора на линии) не обязательно приводит к замедлению передачи по остальным соединениям. В результате совокупное использование загруженных сетей падает.

Для приложения было бы лучше, если бы транспортный протокол вел себя иначе. В идеале задерживаться должны только сегменты первой картинки, тогда как сегменты второй и третьей должны доставляться так, как если бы сегмент первой картинки не был утерян вовсе.

Многопоточный режим SCTP позволяет свести к минимуму блокирование очереди. На рис. 10.3 мы показываем процесс отправки тех же трех изображений. На этот раз сервер использует потоки, так что блокируется только одно изображение, а второе и третье доставляются без помех. Первое изображение не доставляется до тех пор, пока не будет восстановлен порядок сегментов.

Рис. 10.3. Отправка трех изображений по потокам SCTP

Теперь мы можем привести полный код нашего клиента (с функцией sctpstr_cli_echoall, листинг 10.4), чтобы на его примере продемонстрировать устранение проблем с блокированием очереди при помощи SCTP. Новая функция аналогична sctpstr_cli за тем исключением, что клиент больше не требует указания номера потока в квадратных скобках в каждом сообщении. Функция передает сообщение пользователя по всем потокам, количество которых определяется константой SERV_MAX_SCTP_STRM. После отправки сообщения клиент ждет прихода всех ответных сообщений сервера. Запуская сервер, мы передаем ему аргумент командной строки, указывающий на то, что сервер должен отвечать на сообщения по тем же потокам, по которым они приходят. Это позволяет пользователю отслеживать ответы и порядок их прибытия.

Листинг 10.4. Функция sctp_strcliecho

 1 #include "unp.h"


 2 #define SCTP_MAXLINE 800


 3 void

 4 sctpstr_cli_echoall(FILE *fp, int sock_fd, struct sockaddr to,

 5  socklen_t tolen)

 6 {

 7  struct sockaddr_in peeraddr;

 8  struct sctp_sndrcvinfo sri;

 9  char sendline[SCTP_MAXLlNE], recvline[SCTP_MAXLINE];

10  socklen_t len;

11  int rd_sz, i, strsz;

12  int msg_flags;


13  bzero(sendline, sizeof(sendline));

14  bzero(&sri, sizeof(sri));

15  while (fgets(sendline, SCTP_MAXLINE - 9, fp) != NULL) {

16   strsz = strlen(sendline);

17   if (sendline[strsz-1] == 'n') {

18    sendline[strsz-1] = '';

19    strsz--;

20   }

21   for (i=0; i<SERV_MAX_SCTP_STRM; i++) {

22    snprintf(sendline + strsz, sizeof(sendline) - strsz,

23     ".msg %d", i);

24    Sctp_sendmsg(sock_fd, sendline, sizeof(sendline),

25     to, tolen, 0, 0, i, 0, 0);

26   }

27   for (i =0; i < SERV_MAX_SCTP_STRM; i++) {

28    len = sizeof(peeraddr);

29    rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline),

30     (SA*)&peeraddr, &len, &sri, &msg_flags);

31    printf("From str:%d seq:%d (assoc:0x%x)",

32     sri.sinfo_stream, sri.sinfo_ssn,

33     (u_int)sri, sinfo_assoc_id);

34    printf("%.*sn", rd_sz, recvline);

35   }

36  }

37 }

Инициализация структур данных и ожидание ввода

13-15 Как и в предыдущем примере, клиент инициализирует структуру sri, предназначенную для настройки потока, с которым клиент будет работать. Кроме того, клиент обнуляет буфер данных, из которого считывается пользовательский ввод. Затем программа входит в основной цикл, блокируясь в вызове fgets.

Предварительная обработка сообщения

16-20 Клиент определяет размер сообщения и удаляет символ перевода строки, если таковой находится в конце буфера.

Отправка сообщения по всем потокам

21-26 Клиент отсылает сообщение с помощью функции sctp_sendmsg. Передается все содержимое буфера длиной SCTP_MAXLINE. Перед отправкой сообщения к нему добавляется строка .msg, и номер потока, чтобы мы могли впоследствии определить порядок получения сообщений и сравнить его с порядком отправки сообщений. Обратите внимание, что клиент отправляет сообщения по заданному количеству потоков, не проверяя, сколько потоков было согласовано с сервером. Может получиться так, что некоторые операции отправки сообщений завершатся с ошибкой, если количество потоков будет снижено по запросу собеседника.

ПРИМЕЧАНИЕ

Этот код может работать неправильно, если окна приема и отправки будут слишком малы. Если окно приема собеседника слишком мало, клиент может заблокироваться. Поскольку клиент не переходит к считыванию данных из приемного буфера, пока он не отправит все сообщения, сервер также может заблокироваться в ожидании освобождения буфера клиента. В результате обе конечные точки SCTP зависнут. Приведенная программа не рассчитана на масштабирование. Она предназначена лишь для иллюстрации потоков и блокирования очередей в простейшем варианте.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*