UNIX: разработка сетевых приложений - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
29 str = "SHUTDOWN COMPLETE";
30 break;
31 case SCTP_CANT_STR_ASSOC:
32 str = "CAN'T START ASSOC";
33 break;
34 default:
35 str = "UNKNOWN";
36 break;
37 } /* конец ветвления switch (sac->sac_state) */
38 printf("SCTP_ASSOC_CHANGE %s, assoc=0x%xn", str,
39 (uint32_t)sac->sac_assoc_id);
40 break;
41 case SCTP_PEER_ADDR_CHANGE:
42 spc = &snp->sn_paddr_change;
43 switch (spc->spc_state) {
44 case SCTP_ADDR_AVAILABLE:
45 str = "ADDRESS AVAILABLE";
46 break;
47 case SCTP_ADDR_UNREACHABLE:
48 str = "ADDRESS UNREACHABLE";
49 break;
50 case SCTP_ADDR_REMOVED:
51 str = "ADDRESS REMOVED";
52 break;
53 case SCTP_ADDR_ADDED:
54 str = "ADDRESS ADDED";
55 break;
56 case SCTP_ADDR_MADE_PRIM:
57 str = "ADDRESS MADE PRIMARY";
58 break;
59 default:
60 str = "UNKNOWN";
61 break;
62 } /* конец ветвления switch (spc->spc_state) */
63 printf("SCTP_PEER_ADDR_CHANGE %s, addr=%s, assoc=0x%xn", str,
64 Sock_ntop((SA*)&spc->spc_aaddr, sizeof(spc->spc_aaddr)),
65 (uint32_t)spc->spc_assoc_id);
66 break;
67 case SCTP_REMOTE_ERROR:
68 sre = &snp->sn_remote_error;
69 printf("SCTP_REMOTE_ERROR: assoc=0x%x error=%dn",
70 (uint32_t)sre->sre_assoc_id, sre->sre_error);
71 break;
72 case SCTP_SEND_FAILED:
73 ssf = &snp->sn_send_failed;
74 printf("SCTP_SEND_FAILED: assoc=0x%x error=%dn",
75 (uint32_t)ssf->ssf_assoc_id, ssf->ssf_error);
76 break;
77 case SCTP_ADAPTION_INDICATION:
78 ae = &snp->sn_adaption_event;
79 printf("SCTP_ADAPTION_INDICATION: 0x%xn",
80 (u_int)ae->sai_adaption_ind);
81 break;
82 case SCTP_PARTIAL_DELIVERY_EVENT:
83 pdapi = &snp->sn_pdapi_event;
84 if (pdapi->pdapi_indication == SCTP_PARTIAL_DELIVERY_ABORTED)
85 printf("SCTP_PARTIAL_DELIEVERY_ABORTEDn");
86 else
87 printf("Unknown SCTP_PARTIAL_DELIVERY_EVENT 0x%xn",
88 pdapi->pdapi_indication);
89 break;
90 case SCTP_SHUTDOWN_EVENT:
91 sse = &snp->sn_shutdown_event;
92 printf("SCTP_SHUTDOWN_EVENT: assoc=0x%xn",
93 (uint32_t)sse->sse_assoc_id);
94 break;
95 default:
96 printf("Unknown notification event type=0x%xn",
97 snp->sn_header.sn_type);
98 }
99 }
Преобразование буфера и начало ветвления14-15 Функция преобразует буфер вызова к типу union, после чего разыменовывает структуру sn_header и тип sn_type и выполняет ветвление по значению соответствующего поля.
Обработка изменения состояния ассоциации16-40 Если функция обнаруживает в буфере уведомление об изменении ассоциации, она выводит тип происшедшего изменения.
Изменение адреса собеседника16-40 Если получено уведомление об изменении адреса собеседника, функция распечатывает событие и новый адрес.
Ошибка на удаленном узле67-71 Если получено уведомление об ошибке на удаленном узле, функция отображает сообщение об этом вместе с идентификатором ассоциации, для которой получено уведомление. Мы не пытаемся декодировать и отобразить сообщение об ошибке, присланное собеседником. При необходимости эти сведения можно получить из поля sre_data структуры sctp_remote_error.
Ошибка отправки сообщения72-76 Если получено уведомление об ошибке отправки сообщения, мы можем сделать вывод, что сообщение не было отправлено собеседнику. Это означает, что либо ассоциация завершает работу и вскоре будет получено уведомление об изменении ее состояния (если оно еще не было получено) или же сервер использует расширение частичной надежности и отправка сообщения оказалась неудачной из-за наложенных ограничений. Данные, которые все-таки были переданы, помещаются в поле ssf_data, которая наша функция не использует.
Индикация уровня адаптера77-81 Если получено уведомление об уровне адаптера, функция отображает соответствующее 32-разрядное значение, полученное в сообщении INIT или INIT-ACK.
Уведомление механизма частичной доставки82-89 Если получено уведомление механизма частичной доставки, функция выводит на экран соответствующее сообщение. Единственное определенное на момент написания этой книги событие, связанное с частичной доставкой, состоит в ее аварийном завершении.
Уведомление о завершении ассоциации90-94 Если получено уведомление о завершении ассоциации, мы можем сделать вывод, что собеседник выполняет корректное закрытие. За этим уведомлением обычно следует уведомление об изменении состояния ассоциации, которое приходит б момент окончания последовательности пакетов, завершающих ассоциацию. Код сервера, использующего нашу новую функцию, приведен в листинге 23.5.
Листинг 23.5. Сервер, обрабатывающий уведомления о событиях
//sctp/sctpserv06.c
21 bzero(&evnts, sizeof(evnts));
22 evnts.sctp_data_io_event = 1;
23 evnts.sctp_association_event = 1;
24 evnts.sctp_address_event = 1;
25 evnts.sctp_send_failure_event = 1;
26 evnts.sctp_peer_error_event = 1;
27 evnts.sctp_shutdown_event = 1;
28 evnts.sctp_partial_delivery_event = 1;
29 evnts.sctp_adaption_layer_event = 1;
30 Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts));
31 Listen(sock_fd, LISTENQ);
32 for (;;) {
33 len = sizeof(struct sockaddr_in);
34 rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
35 (SA*)&cliaddr, &len, &sri, &msg_flags);
36 if (msg_f1ags & MSG_NOTIFICATION) {
37 print_notification(readbuf);
38 continue;
39 }
Подписка на уведомления21-30 Сервер изменяет параметры подписки на события таким образом, чтобы получать все возможные уведомления.
Получение данных31-35 Эта часть кода сервера осталась неизменной.
Обработка уведомлений36-39 Сервер проверяет поле msg_flags. Если сообщение представляет собой уведомление, сервер вызывает рассмотренную ранее функцию sctp_print_notification и переходит к обработке следующего сообщения.
Запуск программы
Мы запускаем клиент и отправляем одно сообщение.
FreeBSD-lap: ./sctpclient01 10.1.1.5
[0]Hello
From str:1 seq:0 (assoc:c99e15a0):[0]Hello
Control-D
FreeBSD-lap:
Сервер отображает сообщения обо всех происходящих событиях (приеме входящего соединения, получении сообщения, завершении соединения).
FreeBSD-lap:./sctpserv06
SCTP_ADAPTION_INDICATION:0x504c5253
SCTP_ASSOC_CHANGE: COMMUNICATION UP, assoc=c99e2680h
SCTP_SHUTDOWN_EVENT; assoc=c99e2680h
SCTP_ASSOC_CHANGE: SHUTDOWN COMPLETE, assoc=c99e2680h
Control-C
Как видите, сервер действительно выводит сообщения обо всех происходящих событиях транспортного уровня.
23.5. Неупорядоченные данные
В обычном режиме SCTP обеспечивает надежную упорядоченную доставку данных. Кроме того, SCTP предоставляет и сервис надежной неупорядоченной доставки. Сообщение с флагом MSG_UNORDERED отправляется вне очереди и делается доступным для чтения сразу же после приема на удаленном узле. Такое сообщение может быть отправлено по любому потоку. Ему не присваивается порядковый номер внутри какого-либо потока. В листинге 23.6 представлены изменения кода клиента, позволяющие ему отправлять внеочередные запросы серверу.
Листинг 23.6. Функция sctp_strcli, отправляющая внеочередные данные
//sctp/sctp_strcli_un.c
18 out_sz = strlen(sendline);
19 Sctp_sendmsg(sock_fd, sendline, out_sz,
20 to, tolen, 0, MSG_UNORDERED, sri.sinfo_stream, 0, 0);
Отправка внеочередных данных18-20 Функция sctp_str_cli практически не отличается от той, которую мы разработали в разделе 10.4. Единственное изменение произошло в строке 21: клиент передает флаг MSG_UNORDERED, включающий механизм частичной доставки. Обычно все сообщения внутри потока упорядочиваются по номерам. Флаг MSG_UNORDERED позволяет отправить сообщение без порядкового номера. Такое сообщение доставляется адресату сразу после получения его стеком SCTP, даже если другие внеочередные сообщения, отправленные ранее по тому же потоку, еще не были приняты.
23.6. Связывание с подмножеством адресов
Некоторым приложениям требуется связывать один сокет с некоторым конкретным подмножеством всех адресов узла. Протоколы TCP и UDP не позволяют выделить подмножество адресов. Системный вызов bind позволяет приложению связать сокет с единственным адресом или сразу со всеми адресами узла (то есть с универсальным адресом). Поэтому в SCTP был добавлен новый системный вызов sctp_bindx, который позволяет приложению связываться с произвольным количеством адресов. Все адреса должны иметь один и тот же номер порта, а если ранее вызывалась функция bind, то номер порта должен быть таким, как в вызове bind. Если указать не тот порт, вызов sctp_bindx завершится с ошибкой. В листинге 23.7 представлена функция, которую мы добавим к нашему серверу, чтобы получить возможность связывать сокет с адресами, передаваемыми в качестве аргументов командной строки.