Листинг 23.14. Управление периодической проверкой соединения
//sctp/sctp_modify_hb.c
1 #include "unp.h"
2 int
3 heartbeat_action(int sock_fd, struct sockaddr *sa, socklen_t salen,
4 u_int value)
5 {
6 struct sctp_paddrparams sp;
7 int siz;
8 bzero(&sp, sizeof(sp));
9 sp.spp_hbinterval = value;
10 memcpy((caddr_t)&sp, spp_address, sa.salen);
11 Setsockopt(sock_fd, IPPROTO_SCTP,
12 SCTP_PEER_ADDR_PARAMS, &sp, sizeof(sp));
13 return(0);
14 }
8-9
Мы обнуляем структуру sctp_paddrparams
, чтобы случайно не изменить какой-нибудь параметр, который нас не интересует. Затем мы копируем в нее переданное пользователем значение задержки: SCTP_ISSUE_HB
, SCTP_NO_HB
или конкретное число.
10
Функция подготавливает адрес и копирует его в структуру sctp_paddrparams
, чтобы реализация SCTP знала, к какому адресу относятся устанавливаемые нами параметры периодической проверки соединения.
11-12
Наконец, функция делает вызов параметра сокета, чтобы выполнить запрошенную пользователем операцию.
23.10. Выделение ассоциации
Пока что мы занимались исключительно интерфейсом типа «один-ко-многим». Этот интерфейс имеет несколько преимуществ перед традиционным интерфейсом «один-к-одному»:
■ программа работает с единственным дескриптором;
■ программисту достаточно написать простой последовательный сервер;
■ приложение может передавать данные в третьем и четвертом пакетах четырехэтажного рукопожатия, если для неявной установки соединения используются функции sendmsg
и sctp_sendmsg
;
■ отсутствует необходимость в отслеживании состояния на транспортном уровне. Другими словами, приложение просто запрашивает данные из дескриптора сокета, не вызывая традиционных функций connect
и accept
для получения сообщений.
Есть у этого интерфейса и недостатки. Самый существенный из них состоит в том, что интерфейс типа «один-ко-многим» затрудняет написание параллельного сервера (многопоточного или порождающего процессы). Для устранения этого недостатка была придумана функция sctp_peeloff
. Она принимает в качестве аргумента дескриптор сокета типа «один-ко-многим» и идентификатор ассоциации, а возвращает новый дескриптор сокета типа «один-к-одному» с единственной ассоциацией (сохраняя все уведомления и данные, помещенные в очередь этой ассоциации). Исходный сокет остается открытым, причем все остальные ассоциации проведенной операцией извлечения никак не затрагиваются.
Выделенный сокет может быть передан потоку или дочернему процессу для обработки запросов клиента. Листинг 23.15 демонстрирует новую модифицированную версию сервера, который обрабатывает первое сообщение клиента, выделяет ассоциацию при помощи sctp_peeloff
, порождает дочерний процесс и вызывает функцию str_echo
для TCP, которая была написана в разделе 5.3. Адрес из полученного сообщения мы передаем нашей функции из раздела 23.8, которая по этому адресу определяет идентификатор ассоциации. Идентификатор хранится также в поле sri
, sinfo_assoc_id
. Наша функция служит лишь иллюстрацией использования альтернативного метода. Породив процесс, сервер переходит к обработке следующего сообщения.
Листинг 23.15. Параллельный сервер SCTP
//sctp/sctpserv_fork.c
23 for (;;) {
24 len = sizeof(struct sockaddr_in);
25 rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
26 (SA*)&cliaddr, &len, &sri, &msg_flags);
27 Sctp_sendmsg(sock_fd, readbuf, rd_sz,
28 (SA*)&cliaddr, len,
29 sri.sinfo_ppid,
30 sri.sinfo_flags, sn.sinfo_stream, 0, 0);
31 assoc = sctp_address_to_associd(sock_fd, (SA*)&cliaddr, len);
32 if ((int)assoc == 0) {
33 err_ret("Can't get association id");
34 continue;
35 }
36 connfd = sctp_peeloff(sock_fd, assoc);
37 if (connfd == -1) {
38 err_ret("sctp_peeloff fails");
39 continue;
40 }
41 if ((childpid = fork()) == 0) {
42 Close(sock_fd);