unsigned char *CMSG_DATA(struct cmsghdr *<i>cmsgptr</i>);
<i>Возвращает: указатель на первый байт данных, связанных со структурой cmsghdr</i>
unsigned int CMSG_LEN(unsigned int <i>length</i>);
<i>Возвращает: значение, которое записывается в cmsg_len</i>
unsigned int CMSG_SPACE(unsigned int <i>length</i>);
<i>Возвращает: общий размер объекта вспомогательных данных</i>
ПРИМЕЧАНИЕ
В POSIX определены первые пять макросов, а в [113] определены последние два.
Эти макросы могли бы быть использованы в следующем псевдокоде:
struct msghdr msg;
struct cmsghdr *cmsgptr;
/* заполнение структуры msg */
/* вызов recvmsg() */
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level == ... &&
cmsgptr->cmsg_type == ...) {
u_char *ptr;
ptr = CMSG_DATA(cmsgptr);
/* обработка данных, на которые указывает ptr */
}
}
Макрос
CMSG_FIRSTHDR
возвращает указатель на первый объект вспомогательных данных или пустой указатель, если в структуре
msghdr
нет вспомогательных данных (или
msg_control
является пустым указателем, или
cmsg_len
меньше размера структуры
cmsghdr
). Макрос
CMSG_NXTHDR
возвращает пустой указатель, когда в буфере управления нет другого объекта вспомогательных данных.
ПРИМЕЧАНИЕ
Многие существующие реализации макроса CMSG_FIRSTHRD никогда не используют элемент msg_controllen и просто возвращают значение cmsg_control. В листинге 22.2 мы проверяем значение msg_controllen перед вызовом макроопределения.
Разница между макросами
CMSG_LEN
и
CMSG_SPACE
заключается в том, что первый возвращает длину объекта вместе с дополняющими нулями (это значение хранится в
cmsg_len
), а последний возвращает длину собственно объекта (это значение может использоваться для динамического выделения памяти под объект).
14.7. Сколько данных находится в очереди?
Иногда требуется узнать, сколько данных находится в очереди для чтения данного сокета, не считывая эти данные. Для этого имеется три способа.
1. Если нашей целью не является блокирование в ядре (поскольку мы можем выполнять другие задачи, пока данные для чтения еще не готовы), может использоваться неблокируемый ввод-вывод. Мы обсуждаем его в главе 16.
2. Если мы хотим проверить данные, но при этом оставить их в приемном буфере для считывания какой-либо другой частью процесса, мы можем использовать флаг
MSG_PEEK
(см. табл. 14.1). Если мы не уверены, что какие-либо данные готовы для чтения, мы можем объединить этот флаг с отключением блокировки для сокета или с флагом
MSG_DONTWAIT
.
Помните о том, что для потокового сокета количество данных в приемном буфере может изменяться между двумя последовательными вызовами функции
recv
. Например, предположим, что мы вызываем recv для сокета TCP, задавая буфер длиной 1024 и флаг
MSG_PEEK
, и возвращаемое значение равно 100. Если затем мы снова вызовем функцию recv, возможно, возвратится более 100 байт (мы задаем длину буфера больше 100), поскольку в промежутке между двумя нашими вызовами
recv
могли быть получены дополнительные данные.
А что произойдет в случае сокета UDP, когда в приемном буфере имеется дейтаграмма? При вызове
recvfrom
с флагом
MSG_PEEK
, за которым последует другой вызов без задания
MSG_PEEK
, возвращаемые значения обоих вызовов (размер дейтаграммы, ее содержимое и адрес отправителя) будут совпадать, даже если в приемный буфер сокета между двумя вызовами добавляются дополнительные дейтаграммы. (Мы считаем, конечно, что никакой другой процесс не использует тот же дескриптор и не осуществляет чтение из данного сокета в это же время.)
3. Некоторые реализации поддерживают команду
FIONREAD
функции
ioctl
. Третий аргумент функции
ioctl
— это указатель на целое число, а возвращаемое в этом целом числе значение — это текущее число байтов в приемном буфере сокета [128, с. 553]. Это значение является общим числом установленных в очередь байтов, которое для сокета UDP включает все дейтаграммы, установленные в очередь. Также помните о том, что значение, возвращаемое для сокета UDP, в Беркли-реализациях включает пространство, требуемое для структуры адреса сокета, содержащей IP-адрес отправителя и порт для каждой дейтаграммы (16 байт для IP4, 24 байта для IP6).
14.8. Сокеты и стандартный ввод-вывод
Во всех наших примерах мы применяли то, что иногда называется вводом-выводом Unix, вызывали функции
read
и
write
и их разновидности (
recv
,
send
и т.д.). Эти функции работают с дескрипторами и обычно реализуются как системные вызовы внутри ядра Unix.
Другой метод выполнения ввода-вывода заключается в использовании стандартной библиотеки ввода-вывода. Она задается стандартом ANSI С и была задумана как библиотека, совместимая с не-Unix системами, поддерживающими ANSI С. Стандартная библиотека ввода-вывода обрабатывает некоторые моменты, о которых мы должны заботиться сами при использовании функций ввода- вывода Unix, таких как автоматическая буферизация потоков ввода и вывода. К сожалению, ее обработка буферизации потока может представить новый ряд проблем, о которых следует помнить. Глава 5 [110] подробно описывает стандартную библиотеку ввода-вывода, а в [92] представлена полная реализация стандартной библиотеки ввода-вывода и ее обсуждение.
ПРИМЕЧАНИЕ
При обсуждении стандартной библиотеки ввода-вывода используется термин «поток» в выражениях типа «мы открываем поток ввода» или «мы очищаем поток вывода». Не путайте это с подсистемой потоков STREAMS, которую мы обсуждаем в главе 31.