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 设置为 EAGAINEWOULDBLOCK。接收调用通常返回任何可用数据(不超过请求量),而不是等待接收请求的全部数据。

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 起)
启用非阻塞操作;如果操作会导致阻塞,调用将失败并返回错误 EAGAINEWOULDBLOCK(这也可以通过 fcntl(2) 的 F_SETFL 命令配合 O_NONBLOCK 标志来启用)。
MSG_ERRQUEUE (自 Linux 2.2 起)
此标志指定应从套接字错误队列中接收排队的错误。错误通过辅助消息传递,其类型取决于协议(对于 IPv4 为 IP_RECVERR)。用户应提供足够大的缓冲区。有关详细信息,请参阅 cmsg(3) 和 ip(7)。导致错误的原始数据包的有效载荷通过 msg_iovec 作为普通数据传递。导致错误的原始数据报的目的地址通过 msg_name 提供。
对于本地错误,不传递地址(可以通过 cmsghdrcmsg_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 给定一个指向辅助消息的指针,返回一个指向导致错误的网络对象地址的指针。如果该地址未知,则 sockaddrsa_family 成员包含 AF_UNSPEC,且 sockaddr 的其他字段未定义。导致错误的数据包的有效载荷作为普通数据传递。

对于本地错误,不传递地址(可以通过 cmsghdrcmsg_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_namemsg_namelen 在套接字未连接时指定源地址;如果不希望或不需要名称,msg_name 可以作为 NULL 指针给出。字段 msg_iovmsg_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。

错误

以下是套接字层生成的一些标准错误。底层协议模块可能会生成并返回额外错误;请参阅其手册页。

EAGAINEWOULDBLOCK
套接字被标记为非阻塞且接收操作会导致阻塞,或者设置了接收超时且在接收到数据之前超时。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_OOBMSG_PEEKMSG_WAITALL 标志。

说明

上述原型遵循 glibc2。单一 UNIX 规范 (Single UNIX Specification) 与此一致,除了它将返回值的类型定为 ssize_t(而 4.x BSD、libc4 和 libc5 的返回类型均为 int)。flags 参数在 4.x BSD 中为 int,但在 libc4 和 libc5 中为 unsigned intlen 参数在 4.x BSD 中为 int,但在 libc4 和 libc5 中为 size_taddrlen 参数在 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)

引用自

ddp(7), getifaddrs(3), if_nameindex(3), netlink(7), packet(7), raw(7), rds(7), rds-rdma(7), sctp(7), socketcall(2), udp(7)