UNIX: разработка сетевых приложений
23.3. Частичная доставка
Механизм частичной доставки (partial delivery) используется стеком SCTP каждый раз, когда требуется доставить приложению большое сообщение. Сообщение считается «большим», если SCTP решает, что у него недостаточно ресурсов на его обработку. Частичная доставка накладывает на работу SCTP определенные ограничения:
■ объем памяти, занимаемой сообщением в буфере, должен превосходить некоторое пороговое значение;
■ доставка может выполняться только последовательно от начала сообщения до первого отсутствующего блока;
■ после включения механизма частичной доставки приложение не может получить никакие другие сообщения до тех пор, пока «большое» сообщение не будет им полностью считано из буфера. Таким образом, большое сообщение блокирует все остальные, которые в противном случае могли бы быть доставлены (в том числе и по другим потокам).
В реализации SCTP, выполненной группой KAME, используется пороговое значение, равное половине объема приемного буфера сокета. На момент написания этой книги объем приемного буфера по умолчанию составляет 131 072 байта. Если параметр сокета
не меняется, механизм частичной доставки будет включаться только для сообщений, превышающих 65 536 байт. Мы продолжим расширение новой версии сервера из раздела 10.2, написав функцию-обертку для вызоваSO_RCVBUF. Затем мы создадим новый сервер, который будет использовать эту функцию. В листинге 23.2 представлена функция-обертка, способная работать с механизмом частичной доставки.sctp_recvmsgЛистинг 23.2. Работа с API частичной доставки
//sctp/sctp_pdapirev.c1 #include "unp.h"2 static uint8_t *sctp_pdapi_readbuf=NULL;3 static int sctp_pdapi_rdbuf_sz=0;4 uint8_t*5 pdapi_recvmsg(int sock_fd,6 int *rdlen,7 SA *from,8 int *from_len, struct sctp_sndrcvinfo *sri, int *msg_flags)9 {10 int rdsz, left, at_in_buf;11 int frmlen=0;12 if (sctp_pdapi_readbuf == NULL) {13 sctp_pdapi_readbuf = (uint8_t*)Malloc(SCTP_PDAPI_INCR_SZ);14 sctp_pdapi_rdbuf_sz = SCTP_PDAPI_INCR_SZ;15 }16 at_in_buf = Sctp_recvmsg(sock_fd, sctp_pdapi_readbuf, sctp_pdapi_rdbuf_sz,17 from, from_len,18 sri.msg_flags);19 if (at_in_buf < 1) {20 *rdlen = at_in_buf;21 return(NULL);22 }23 while ((*msg_flags & MSG_EOR) == 0) {24 left = sctp_pdapi_rdbuf_sz = at_in_buf;25 if (left < SCTP_PDAPI_NEED_MORE_THRESHOLD) {26 sctp_pdapi_readbuf =27 realloc(sctp_pdapi_readbuf,28 setp_pdapi_rdbuf_sz + SCTP_PDAPI_INCR_SZ);29 if (sctp_pdapi_readbuf == NULL) {30 err_quit("sctp_pdapi ran out of memory");31 }32 sctp_pdapi_rdbuf_sz += SCTP_PDAPI_INCR_SZ;33 left = sctp_pdapi_rdbuf_sz - at_in_buf;3435 rdsz = Sctp_recvmsg(sock_fd, &sctp_pdapi_readbuf[at_in_buf],36 left, NULL, &frmlen, NULL, msg_flags);37 at_in_buf += rdsz;38 }39 *rdlen = at_in_buf;40 return(sctp_pdapi_readbuf);41 }Подготовка статического буфераЕсли статический буфер не выделен, функция выделяет его и инициализирует переменную, хранящую информацию о состоянии этого буфера.12-15Чтение сообщенияПервое сообщение считывается из сокета вызовом16-18.sctp_recvmsgОбработка ошибки чтенияЕсли19-22возвращает ошибку или признак конца файла EOF, соответствующий код возвращается вызвавшему нашу функцию процессу без всяких изменений.sctp_recvmsgЕсли сообщение считано не полностьюЕсли флаги сообщения показывают, что оно было считано не полностью, мы вызываем функцию23-24снова. Предварительно мы вычисляем объем свободного места в буфере.sctp_recvmsgПроверка необходимости увеличения статического буфераЕсли остаток приемного буфера оказался меньше некоторого минимального значения, этот буфер необходимо увеличить. С этой целью мы вызываем функцию25-34, выделяющую буфер большего размера, после чего копируем в новый буфер данные из старого буфера. Если по какой-то причине размер буфера не может быть увеличен, функция завершает свою работу с выводом сообщения об ошибке.reallocПолучение данныхНовые данные считываются из буфера вызовом35-36.sctp_recvmsgШаг впередФункция увеличивает индекс буфера, после чего возвращается на проверку полного считывания сообщения.37-38После завершения циклаПосле завершения цикла функция копирует количество считанных байтов в буфер, указатель на который передается ей вызвавшим процессом, и возвращает этому процессу указатель на собственный буфер.39-40Теперь мы можем изменить сервер SCTP таким образом, чтобы он использовал нашу новую функцию. Новый код представлен в листинге 23.3.
Листинг 23.3. Сервер SCTP, использующий API частичной доставки
//sctp/sctpserv05.c26 for (;;) {27 len = sizeof(struct sockaddr_in);28 bzero(&sri,.sizeof(sri));