Литмир - Электронная Библиотека
Содержание  
A
A

Если сервер хочет закрыть ассоциацию после отправки сообщения, он должен добавить флаг MSG_EOF в это сообщение, поместив его в поле

sinfo_flags
структуры
sctp_sndrcvinfo
. Этот флаг закрывает ассоциацию после подтверждения приема отсылаемого сообщения. Альтернативный метод состоит в установке флага
MSG_ABORT
в том же поле
sinfo_flags
. При этом происходит немедленное закрытие ассоциации с отправкой порции ABORT (аналог TCP-сегмента RST). Данные, находящиеся в буфере отправки, сбрасываются. Однако закрытие сеанса SCTP порцией ABORT не приводит к негативным последствиям типа пропущенного состояния TIME_WAIT, как это происходит в TCP. В листинге 10.7 показана новая версия эхо-сервера, инициирующая корректное завершение соединения одновременно с отправкой эхо-ответа клиенту. В листинге 10.8 показана версия клиента, отправляющая порцию ABORT перед закрытием сокета.

Листинг 10.7. Сервер, закрывающий ассоциацию после отправки ответа

//sctp/sctpserv03.c

25 for (;;) {

26  len = sizeof(struct sockaddr_in);

27  rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),

28   (SA*)&cliaddr, &len, &sri, &msg_flags);

29  if (stream_increment) {

30   sri.sinfo_stream++;

31   if (sri.sinfo_stream >=

32    sctp_get_no_strms(sock_fd, (SA*)&cliaddr, len))

33    sri.sinfo_stream = 0;

34  }

35  Sctp_sendmsg(sock_fd, readbuf, rd_sz,

36   (SA*)&cliaddr, len,

37   sri.sinfo_ppid,

38   (sri.sinfo_flags | MSG_EOF), sri.sinfo_stream, 0, 0);

39 }

Отправка ответа с закрытием ассоциации

38
 Изменение кода сервера состоит в том, что мы добавляем флаг
MSG_EOF
к прочим флагам в вызове
sctp_sendmsg
операцией логического ИЛИ. Благодаря этому сервер закрывает ассоциацию после подтверждения доставки сообщения.

Листинг 10.8. Клиент, выполняющий аварийное закрытие ассоциации

//sctp/sctpclient02.c

25 if (echo_to_all == 0)

26  sctpstr_cli(stdin, sock_fd, (SA*)&servaddr, sizeof(servaddr));

27 else

28  sctpstr_cli_echoall(stdin, sock_fd, (SA*)&servaddr,

29   sizeof(servaddr));

30 strcpy(byemsg, "goodbye");

31 Sctp_sendmsg(sock_fd, byemsg, strlen(byemsg),

32  (SA*)&servaddr, sizeof(servaddr), 0, MSG_ABORT, 0, 0, 0);

33 Close(sock_fd);

Аварийное закрытие ассоциации

30-32
 Клиент подготавливает сообщение об аварийном закрытии ассоциации, вызванном пользовательской ошибкой. Затем функция
sctp_sendmsg
вызывается с флагом
MSG_ABORT
. При этом отправляется порция данных ABORT, что приводит к немедленному закрытию ассоциации. В порцию данных включается код пользовательской ошибки и сообщение («goodbye») в поле причины ошибки вышележащего уровня.

Закрытие дескриптора сокета

33
 Хотя ассоциация и была завершена, дескриптор сокета все равно закрыть нужно, чтобы освободить связанные с ним системные ресурсы.

10.8. Резюме

Мы изучили простой пример клиента и сервера SCTP общим объемом около 150 строк кода. Обе программы работали с сокетами SCTP типа «один-ко-многим». Сервер был написан в последовательном стиле, который часто используется при работе с такими сокетами. Он считывал сообщения и отвечал на них по тому же потоку, из которого они приходили, или по потоку с увеличенным на единицу номером. Затем мы исследовали проблему блокирования очереди, изменив программу клиента таким образом, чтобы подчеркнуть особенности ситуации и продемонстрировать использование потоков SCTP для решения проблемы. После этого мы показали, каким образом можно изменить количество потоков при помощи одного из множества параметров сокета, используемых для управления поведением SCTP. Наконец, мы снова изменили код сервера и клиента, чтобы показать корректное и аварийное закрытие ассоциации.

Углубленное исследование SCTP будет проведено в главе 23.

Упражнения

1. Что произойдет с программой в листинге 10.1, если SCTP вернет сообщение об ошибке? Каким образом вы можете устранить указанный недостаток программы?

2. Что произойдет, если сервер завершит работу, не ответив на сообщения? Может ли клиент каким-либо образом получить уведомление об этом событии?

3. В листинге 10.7 в строке 22 аргумент

out_sz
устанавливается равным 800 байт. Как вы думаете, почему мы выбрали именно это значение? Существует ли лучший способ найти оптимальное значение этого аргумента?

4. Как повлияет алгоритм Нагла (см. раздел 7.10) на нашего клиента из листинга 10.7? Не лучше ли будет отключить алгоритм Нагла для этой программы? Воплотите это изменение в код клиента и сервера.

5. В разделе 10.6 мы утверждали, что приложению следует изменять количество потоков до установки ассоциации. Что произойдет в противном случае?

6. Когда мы говорили о количестве потоков, мы подчеркнули, что только для сокетов типа «один-ко-многим» можно увеличить количество потоков при помощи вспомогательных данных. Почему это так? (Подсказка: вспомогательные данные необходимо передавать с сообщениями.)

7. Почему сервер может не отслеживать открытые ассоциации? Опасно ли это?

8. В разделе 10.7 мы изменили сервер так, что он стал закрывать ассоциацию после отправки каждого сообщения. Вызовет ли это какие-либо проблемы? Хорошее ли это решение с точки зрения архитектуры приложения?

Глава 11

Преобразования имен и адресов

11.1. Введение

Во всех предшествующих примерах мы использовали численные адреса узлов (например, 206.6.226.33) и численные номера портов для идентификации серверов (например, порт 13 для стандартного сервера времени и даты и порт 9877 для нашего эхо-сервера). Однако по ряду соображений предпочтительнее использовать имена вместо чисел: во-первых, имена проще запоминаются, во-вторых, если численный адрес поменяется, имя можно сохранить, и в-третьих, с переходом на IPv6 численные адреса становятся значительно длиннее, что увеличивает вероятность ошибки при вводе адреса вручную. В этой главе описываются функции, выполняющие преобразование имен и адресов:

gethostbyname
и
gethostbyaddr
для преобразования имен узлов и IP-адресов, и
getservbyname
и
getservbyport
для преобразования имен служб и номеров портов. Здесь же мы рассмотрим две независимые от протоколов функции
getaddrinfo
и
getnameinfo
, осуществляющие преобразование между IP-адресами и именами узлов, а также между именами служб и номерами портов.

120
{"b":"225366","o":1}