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);
Системные вызовы recv(), recvfrom() и recvmsg() используются для получения сообщений из сокета. Они могут использоваться для получения данных, независимо от того, является ли сокет ориентированным на соединения или нет. В этой странице сперва описаны общие свойства всех трёх системных вызовов, а затем описываются различия между ними.
Вызов recv() отличается от read(2) только наличием аргумента flags. Если значение flags равно нулю, то вызов recv() эквивалентен read(2) (но смотрите ЗАМЕЧАНИЯ). Также, вызов
recv(sockfd, buf, len, flags);
эквивалентен
recvfrom(sockfd, buf, len, flags, NULL, NULL);
При успешном выполнении все три вызова возвращают длину сообщения. Если сообщение слишком длинное и не поместилось в предоставленный буфер, лишние байты могут быть отброшены, в зависимости от типа сокета, на котором принимаются сообщения.
Если на сокете не доступно ни одного сообщения, то обсуждаемые вызовы ожидают их прибытия, если сокет не помечен как неблокирующий (см. fcntl(2)), в противном случае возвращается значение -1, а внешняя переменная errno устанавливается в значение EAGAIN или EWOULDBLOCK. Все эти вызовы обычно сразу возвращают все доступные данные вплоть до запрошенного объема, а не ждут, пока появятся данные полной запрошенной длины.
Для определения появления новых данных в сокете приложение может использовать select(2), poll(2) или epoll(7).
Аргумент flags формируется с помощью объединения логической операцией ИЛИ одного или более следующих значений:
#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 *);
Вызов recvfrom() помещает принятое сообщение в буфер buf. Вызывающий должен указать размер буфера в len.
Если значение src_addr не равно NULL, и в нижележащем протоколе используется адрес источника сообщения, то адрес источника помещается в буфер, указанный в src_addr. В этом случае addrlen является аргументом-результатом. Перед вызовом ему должно быть присвоено значение длины буфера, связанного с src_addr. При возврате addrlen обновляется и содержит действительный размер адреса источника. Возвращаемый адрес обрезается, если предоставленный буфер слишком мал; в этом случае addrlen будет содержать значение большее, чем указывалось в вызове.
Если вызывающему адрес источника не нужен, то значение src_addr и addrlen должно быть равно NULL.
Вызов recv(), обычно, используется только на соединённом сокете (смотрите connect(2)). Он идентичен вызову:
recvfrom(fd, buf, len, flags, NULL, 0);
Для минимизации количества передаваемых аргументов в вызов 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_namelen будет содержаться длина возвращаемого адреса. Если приложению не нужно знать адрес источника, то в msg_name можно указать NULL.
В полях msg_iov и msg_iovlen описываются место приёма/передачи, обсуждаемые в readv(2).
Поле msg_control длиной msg_controllen указывает на буфер для других сообщений, связанных с управлением протоколом или на буфер для разнообразных вспомогательных данных. При вызове recvmsg() в поле msg_controllen должен указываться размер доступного буфера, чей адрес передан в msg_control; при успешном выполнении вызова в этом параметре будет находиться длина последовательности контрольных сообщений.
Сообщения имеют следующий вид:
struct cmsghdr { size_t cmsg_len; /* счетчик байтов данных с заголовком (тип — socklen_t в POSIX) */ int cmsg_level; /* начальный протокол */ int cmsg_type; /* тип, зависящий от протокола */ /* после unsigned char cmsg_data[]; */ };
К вспомогательным данным нужно обращаться только с помощью макросов, определённых в cmsg(3).
Например, этот механизм вспомогательных данных используется в Linux для передачи расширенных ошибок, флагов IP и файловых дескрипторов через доменные сокеты Unix.
При возврате из recvmsg() устанавливается значение поля msg_flags в msghdr. Оно может содержать несколько флагов:
Эти вызовы возвращают количество принятых байт или -1, если произошла ошибка. В случае ошибки в errno записывается код ошибки.
Когда ответная сторона потока выполняет корректное отключение (shutdown), то возвращается 0 (обычный возврат «конец файла»).
В датаграмных сокетах некоторых доменов (например, доменах UNIX и Internet) разрешены датаграммы нулевой длины. При получении такой датаграммы возвращается значение 0.
Также значение 0 может возвращаться, если запрошенное количество принимаемых байт из потокового сокета равно 0.
Здесь представлено несколько стандартных ошибок, возвращаемых с уровня сокетов. Могут также появиться другие ошибки, возвращаемые из соответствующих протокольных модулей; их описание находится в соответствующих справочных страницах.
POSIX.1-2001, POSIX.1-2008, 4.4BSD (эти интерфейсы впервые появились в 4.2BSD).
В POSIX.1 описаны только флаги MSG_OOB, MSG_PEEK и MSG_WAITALL.
Если ожидает дейтаграмма нулевой длины, то read(2) и recv() с нулевым аргументом flags работают по-разному. В этом случае read(2) ничего не делает (дейтаграмма продолжает ждать), а recv() обрабатывает ожидающую дейтаграмму.
Тип socklen_t появился из POSIX. Также смотрите accept(2).
В соответствие с POSIX.1 поле 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), ip(7), ipv6(7), socket(7), tcp(7), udp(7), unix(7)
2017-09-15 | Linux |