recvmsg(2) - Linux 手册页
名称
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)),其作用等同于 src_addr 参数为 NULL 的 recvfrom()。
这三个例程在成功完成时都会返回消息的长度。如果消息过长而无法放入提供的缓冲区,则根据接收消息的套接字类型,多出的字节可能会被丢弃。
如果套接字上没有可用消息,接收调用会等待消息到达,除非套接字是非阻塞的(参见 fcntl(2)),在这种情况下,调用返回 -1,并将外部变量 errno 设置为 EAGAIN 或 EWOULDBLOCK。接收调用通常返回任何可用数据(不超过请求量),而不是等待接收请求的全部数据。
select(2) 或 poll(2) 调用可用于确定何时有更多数据到达。
recv() 调用的 flags 参数由以下一个或多个值通过 OR 操作组合而成:
- MSG_CMSG_CLOEXEC (仅限 recvmsg();自 Linux 2.6.23 起)
- 为通过 UNIX 域文件描述符使用 SCM_RIGHTS 操作(参见 unix(7))接收到的文件描述符设置执行时关闭 (close-on-exec) 标志。该标志的用途与 open(2) 的 O_CLOEXEC 标志相同。
- MSG_DONTWAIT (自 Linux 2.2 起)
- 启用非阻塞操作;如果操作会导致阻塞,调用将失败并返回错误 EAGAIN 或 EWOULDBLOCK(这也可以通过 fcntl(2) 的 F_SETFL 命令配合 O_NONBLOCK 标志来启用)。
- MSG_ERRQUEUE (自 Linux 2.2 起)
- 此标志指定应从套接字错误队列中接收排队的错误。错误通过辅助消息传递,其类型取决于协议(对于 IPv4 为 IP_RECVERR)。用户应提供足够大的缓冲区。有关详细信息,请参阅 cmsg(3) 和 ip(7)。导致错误的原始数据包的有效载荷通过 msg_iovec 作为普通数据传递。导致错误的原始数据报的目的地址通过 msg_name 提供。
- 对于本地错误,不传递地址(可以通过 cmsghdr 的 cmsg_len 成员进行检查)。对于错误接收,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; /* error number */ uint8_t ee_origin; /* where the error originated */ uint8_t ee_type; /* type */ uint8_t ee_code; /* code */ uint8_t ee_pad; /* padding */ uint32_t ee_info; /* additional information */ uint32_t ee_data; /* other data */ /* More data may follow */ }; struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *); - ee_errno 包含排队错误的 errno 编号。ee_origin 是错误起源的原始代码。其他字段是特定于协议的。宏 SOCK_EE_OFFENDER 给定一个指向辅助消息的指针,返回一个指向导致错误的网络对象地址的指针。如果该地址未知,则 sockaddr 的 sa_family 成员包含 AF_UNSPEC,且 sockaddr 的其他字段未定义。导致错误的数据包的有效载荷作为普通数据传递。
对于本地错误,不传递地址(可以通过 cmsghdr 的 cmsg_len 成员进行检查)。对于错误接收,MSG_ERRQUEUE 会在 msghdr 中设置。在传递一个错误后,挂起的套接字错误会根据下一个排队的错误重新生成,并将通过下一次套接字操作传递。
- MSG_OOB
- 此标志请求接收带外数据,这些数据不会在正常数据流中接收。某些协议将加急数据置于正常数据队列的头部,因此对于此类协议不能使用此标志。
- MSG_PEEK
- 此标志导致接收操作返回接收队列开头的数据,而不从队列中移除这些数据。因此,后续的接收调用将返回相同的数据。
- MSG_TRUNC (自 Linux 2.2 起)
- 用于原始 (AF_PACKET)、Internet 数据报 (自 Linux 2.4.27/2.6.8 起)、netlink (自 Linux 2.6.22 起) 和 UNIX 数据报 (自 Linux 3.4 起) 套接字:即使数据包或数据报长于所提供的缓冲区,也会返回其实际长度。未针对 UNIX 域 (unix(7)) 套接字实现。
关于 Internet 流式套接字的使用,请参阅 tcp(7)。
- MSG_WAITALL (自 Linux 2.2 起)
- 此标志请求操作阻塞直到完整请求得到满足。然而,如果捕获到信号、发生错误或断开连接,或者下一个要接收的数据类型与返回的数据类型不同,调用仍可能返回少于请求的数据量。
- recvmsg() 调用使用 msghdr 结构来减少直接提供的参数数量。该结构在 <sys/socket.h> 中定义如下:
-
struct iovec { /* Scatter/gather array items */ void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ }; - 这里 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; /* data byte count, including hdr */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char cmsg_data[]; */ }; - 辅助数据只能通过 cmsg(3) 中定义的宏进行访问。
例如,Linux 使用此辅助数据机制在 UNIX 域套接字上传递扩展错误、IP 选项或文件描述符。
msghdr 中的 msg_flags 字段在 recvmsg() 返回时被设置。它可以包含以下几个标志:
- MSG_EOR
- 表示记录结束;返回的数据完成了一个记录(通常用于类型为 SOCK_SEQPACKET 的套接字)。
- MSG_TRUNC
- 表示由于数据报大于所提供的缓冲区,数据报的尾部被丢弃。
- MSG_CTRUNC
- 表示由于辅助数据缓冲区空间不足,部分控制数据被丢弃。
- MSG_OOB
- 返回此值以表示已接收到加急或带外数据。
- MSG_ERRQUEUE
- 表示没有接收到数据,而是接收到了来自套接字错误队列的扩展错误。
返回值
这些调用返回接收到的字节数,如果发生错误则返回 -1。当对端执行了有序关闭时,返回值将为 0。
错误
以下是套接字层生成的一些标准错误。底层协议模块可能会生成并返回额外错误;请参阅其手册页。
- EAGAIN 或 EWOULDBLOCK
- 套接字被标记为非阻塞且接收操作会导致阻塞,或者设置了接收超时且在接收到数据之前超时。POSIX.1-2001 允许在此情况下返回任一错误,并且不要求这些常量具有相同的值,因此可移植应用程序应同时检查这两种可能性。
- EBADF
参数 sockfd 是一个无效的描述符。
- ECONNREFUSED
- 远程主机拒绝网络连接(通常是因为未运行所请求的服务)。
- EFAULT
接收缓冲区指针指向进程地址空间之外。
EINTR
在接收到任何数据之前,接收被信号传递所中断;参见 signal(7)。
EINVAL
传递了无效参数。
ENOMEM
无法为 recvmsg() 分配内存。
- ENOTCONN
- 套接字与面向连接的协议相关联,但尚未连接(参见 connect(2) 和 accept(2))。
- ENOTSOCK
- 参数 sockfd 没有指向一个套接字。
符合
4.4BSD(这些函数调用最早出现在 4.2BSD 中),POSIX.1-2001。
POSIX.1-2001 仅描述了 MSG_OOB、 MSG_PEEK 和 MSG_WAITALL 标志。
说明
上述原型遵循 glibc2。单一 UNIX 规范 (Single UNIX Specification) 与此一致,除了它将返回值的类型定为 ssize_t(而 4.x BSD、libc4 和 libc5 的返回类型均为 int)。flags 参数在 4.x BSD 中为 int,但在 libc4 和 libc5 中为 unsigned int。len 参数在 4.x BSD 中为 int,但在 libc4 和 libc5 中为 size_t。addrlen 参数在 4.x BSD、libc4 和 libc5 中为 int *。目前使用的 socklen_t * 是由 POSIX 发明的。另请参阅 accept(2)。
根据 POSIX.1-2001,msghdr 结构的 msg_controllen 字段类型应为 socklen_t,但 glibc 目前将其定为 size_t。
有关可用于在单次调用中接收多个数据报的 Linux 特有系统调用,请参阅 recvmmsg(2)。
示例
recvfrom() 的使用示例如 getaddrinfo(3) 所示。
参见
fcntl(2), getsockopt(2), read(2), recvmmsg(2), select(2), shutdown(2), socket(2), cmsg(3), sockatmark(3), socket(7)