Scroll to navigation

RECV(2) Руководство программиста Linux RECV(2)

ИМЯ

recv, recvfrom, recvmsg - принимает сообщение из сокета

ОБЗОР

#include <sys/types.h>

#include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

ОПИСАНИЕ

Системные вызовы recvfrom() и recvmsg() используются для получения сообщений из сокета, и могут использоваться для получения данных, независимо от того, является ли сокет ориентированным на соединения или нет.

Если значение src_addr не равно NULL, и в нижележащем протоколе используется адрес источника, то указывается этот адрес источника. Если значение src_addr равно NULL, то ничего не указывается; в этом случае addrlen не используется и его значение также должно быть равно NULL. Через аргумент addrlen возвращается результат, поэтому вызывающий должен инициализировать его значение перед вызовом, указав размер буфера, связанного с src_addr. При возврате его значение содержит действительный размер адреса источника. Возвращаемый адрес обрезается, если предоставленный буфер слишком мал; в этом случае addrlen будет содержать значение большее, чем указывалось в вызове.

Вызов recv() обычно используется только на соединённом сокете (см. connect(2)); он идентичен вызову recvfrom() с NULL в качестве значения аргумента src_addr.

При успешном выполнении все три вызова возвращают длину сообщения. Если сообщение слишком длинное и не поместилось в предоставленный буфер, лишние байты могут быть отброшены, в зависимости от типа сокета, на котором принимаются сообщения.

Если на сокете не доступно ни одного сообщения, то обсуждаемые вызовы ожидают их прибытия, если сокет не помечен как неблокирующий (см. fcntl(2)), в противном случае возвращается значение -1, а внешняя переменная errno устанавливается в значение EAGAIN или EWOULDBLOCK. Все эти вызовы обычно сразу возвращают все доступные данные вплоть до запрошенного объема, а не ждут, пока появятся данные полной запрошенной длины.

Для определения появления новых данных можно использовать вызов select(2) или poll(2).

Аргумент flags системного вызова recv() формируется с помощью объединения логической операцией ИЛИ одного или более следующих значений:

Установить флаг close-on-exec для файлового дескриптора, полученного через доменный файловый дескриптор UNIX, с помощью операции SCM_RIGHTS (описана в unix(7)). Этот флаг полезен по тем же причинам что и флаг O_CLOEXEC у open(2).
Включить неблокирующий режим. Если операция могла бы привести к блокировке, возвращается EAGAIN или EWOULDBLOCK (этот режим можно также включить с помощью флага O_NONBLOCK и операции F_SETFL через вызов fcntl(2)).
Указание этого флага позволяет получить из очереди ошибок сокета накопившиеся ошибки. Ошибка передаётся в вспомогательном сообщении тип которого зависит от протокола (для IPv4 это IP_RECVERR). Вызывающий должен предоставить буфер достаточного размера. Дополнительная информация приведена в cmsg(3) и ip(7). Содержимое исходного пакета, который привёл к ошибке, передаётся в виде обычных данных через msg_iovec. Исходный адрес назначения датаграммы, которая привела к ошибке, передаётся через msg_name.
Для локальных ошибок адрес не передаётся (это можно выяснить, проверив поле cmsg_len структуры cmsghdr). Для ошибок при приёме устанавливается флаг MSG_ERRQUEUE в msghdr. После того, как ошибка передана программе, следующая ошибка в очереди ошибок становится ожидающей ошибкой и передается программе при следующей операции на сокете.

Ошибка передаётся в виде структуры sock_extended_err:

#define SO_EE_ORIGIN_NONE    0
#define SO_EE_ORIGIN_LOCAL   1
#define SO_EE_ORIGIN_ICMP    2
#define SO_EE_ORIGIN_ICMP6   3
struct sock_extended_err
{

uint32_t ee_errno; /* номер ошибки */
uint8_t ee_origin; /* источник её происхождения */
uint8_t ee_type; /* тип */
uint8_t ee_code; /* код */
uint8_t ee_pad; /* заполнение для выравнивания */
uint32_t ee_info; /* дополнительная информация */
uint32_t ee_data; /* прочие данные */
/* далее могут содержаться ещё данные */ }; struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *);

В ee_errno содержится значение errno для ожидающей ошибки. В ee_origin содержится источник происхождения ошибки. Смысл остальных полей зависит от протокола. Макрос SOCK_EE_OFFENDER возвращает указатель на адрес сетевого объекта, породившего ошибку. Если этот адрес неизвестен, то поле sa_family структуры sockaddr содержит значение AF_UNSPEC, а прочие поля структуры sockaddr не определены. Содержимое пакета, вызвавшего ошибку, передаётся в виде обычных данных.
Для локальных ошибок адрес не передаётся (это можно выяснить, проверив поле cmsg_len структуры cmsghdr). Для ошибок при приёме устанавливается флаг MSG_ERRQUEUE в msghdr. После того, как ошибка передана программе, следующая ошибка в очереди ошибок становится ожидающей ошибкой и передается программе при следующей операции на сокете.
Этот флаг запрашивает приём внепотоковых данных, которые в противном случае не были бы получены в обычном потоке данных. Некоторые протоколы помещают данные повышенной срочности в начало очереди с обычными данными, и поэтому этот флаг не может использоваться с такими протоколами.
Этот флаг заставляет выбрать данные из начала очереди приёма, но не удалять их оттуда. Таким образом, последующий вызов вернёт те же самые данные.
Для raw (AF_PACKET), датаграмм Интернета (начиная с Linux 2.4.27/2.6.8) и netlink (начиная с Linux 2.6.22) возвращает реальную длину пакета датаграммы, даже если она была больше, чем предоставленный буфер. Не реализован для доменных сокетов UNIX (unix(7)).

Описание использования с потоковым сокетами Интернета смотрите в tcp(7).

Этим флагом включается блокирование операции до полной обработки запроса. Однако, этот вызов всё равно может вернуть меньше данных, чем было запрошено, если был пойман сигнал, произошла ошибка или разрыв соединения, или если начали поступать данные другого типа, не того, который был сначала.

Для минимизации количества передаваемых аргументов в вызов recvmsg() используется структура msghdr. Она определена в <sys/socket.h> следующим образом:

struct iovec {                    /* массив элементов приёма/передачи */

void *iov_base; /* начальный адрес */
size_t iov_len; /* количество передаваемых байт */ }; struct msghdr {
void *msg_name; /* необязательный адрес */
socklen_t msg_namelen; /* размер адреса */
struct iovec *msg_iov; /* массив приёма/передачи */
size_t msg_iovlen; /* количество элементов в msg_iov */
void *msg_control; /* вспомогательные данные,
см. ниже */
size_t msg_controllen; /* размер буфера вспомогательных
данных */
int msg_flags; /* флаги принятого сообщения */ };

Здесь в msg_name и msg_namelen указывается адрес, если если сокет не соединён; в параметре msg_name можно передать указатель NULL, если имена не требуются или вообще нежелательны. В полях msg_iov и msg_iovlen описываются место приёма/передачи, обсуждаемые в readv(2). Поле msg_control длиной msg_controllen указывает на буфер для других сообщений, связанных с управлением протоколами или на буфер для разнообразных вспомогательных данных. При вызове recvmsg() в поле msg_controllen должен указываться размер доступного буфера, чей адрес передан в msg_control; при успешном завершении вызова в этом параметре будет находиться длина последовательности контрольных сообщений.

Сообщения имеют следующий вид:

struct cmsghdr {

socklen_t cmsg_len; /* количество байт данных, включая hdr */
int cmsg_level; /* изначальный протокол */
int cmsg_type; /* тип, зависящий от протокола */ /* дальше следует
unsigned char cmsg_data[]; */ };

К вспомогательным данным нужно обращаться только с помощью макросов, определённых в cmsg(3).

Например, этот механизм вспомогательных данных используется в Linux для передачи расширенных ошибок, флагов IP и файловых дескрипторов через доменные сокеты Unix.

При возврате из recvmsg() устанавливается значение поля msg_flags в msghdr. Оно может содержать несколько флагов:

означает конец записи: возвращённые данные заканчивают запись (обычно используется вместе с сокетами типа SOCK_SEQPACKET).
означает, что хвостовая часть датаграммы была отброшена, потому что датаграмма была больше, чем предоставленный буфер.
означает, что часть управляющих данных была отброшена из-за недостатка места в буфере вспомогательных данных.
возвращается для индикации получения внепотоковых данных.
означает, что были получены не данные, а расширенное сообщение об ошибке из очереди ошибок сокета.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Эти вызовы возвращают количество принятых байт или -1, если произошла ошибка. При корректном выполнении отключения удалённой стороной возвращается 0.

ОШИБКИ

Здесь представлено несколько стандартных ошибок, возвращаемых с уровня сокетов. Могут также появиться другие ошибки, возвращаемые из соответствующих протокольных модулей; их описание находится в соответствующих справочных страницах.

Сокет помечен как неблокируемый, а операция приёма привела бы к блокировке, или установлено время ожидания данных и это время истекло до получения данных. Согласно POSIX.1-2001 в этом случае может возвращаться любая ошибка и не требуется, чтобы эти константы имели одинаковое значение, поэтому переносимое приложение должно проверить оба случая.
Аргумент sockfd содержит неверный дескриптор.
Удалённый узел отказался устанавливать сетевое соединение (обычно потому, что там не работает запрошенная служба).
Указатель на приёмный буфер указывает вне адресного пространства процесса.
Приём данных был прерван сигналом, а данные ещё не были доступны; см. signal(7).
Передан неверный аргумент.
Не удалось выдели память для recvmsg().
Сокет, связанный с протоколом, ориентированным на соединение, не был соединён (см. connect(2) и accept(2)).
Аргумент sockfd не указывает на сокет.

СООТВЕТСТВИЕ СТАНДАРТАМ

4.4BSD (эти системные вызовы впервые появились в 4.2BSD), POSIX.1-2001.

В POSIX.1-2001 описаны только флаги MSG_OOB, MSG_PEEK и MSG_WAITALL.

ЗАМЕЧАНИЯ

Вышеприведенные прототипы соответствуют glibc2. Они соответствуют стандарту Single UNIX Specification, за исключением того, что там возвращаемые значения имеют тип ssize_t (тогда как в BSD 4.x, libc4 и libc5 имеют тип int). Аргумент flags в BSD 4.x имеет тип int, а в libc4 и libc5 — unsigned int. Аргумент len в BSD 4.x имеет тип int, а в libc4 и libc5 — size_t. В 4.x BSD, libc4 и libc5 аргумент addrlen имеет тип int *. Для соответствия POSIX у данных вызовов он имеет тип socklen_t *. Смотрите также accept(2).

В соответствие с POSIX.1-2001 поле msg_controllen структуры msghdr должно иметь тип socklen_t, но в настоящее время в glibc оно имеет тип size_t.

В recvmmsg(2) можно найти информацию о специальном системном вызове Linux, который можно использовать для приёма нескольких датаграмм за один вызов.

ПРИМЕР

Пример использования recvfrom() показан в getaddrinfo(3).

СМОТРИТЕ ТАКЖЕ

fcntl(2), getsockopt(2), read(2), recvmmsg(2), select(2), shutdown(2), socket(2), cmsg(3), sockatmark(3), socket(7)

2011-09-16 Linux