RECVMMSG - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
recvmmsg-在套接字上接收多个消息
语法
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <sys/socket.h> int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
说明
recvmmsg()系统调用是recvmsg(2)的扩展,允许调用者使用单个系统调用从套接字接收多个消息。 (这对某些应用程序具有性能上的好处。)对recvmsg(2)的进一步扩展是支持接收操作超时。
sockfd参数是用于接收数据的套接字的文件描述符。
msgvec参数是指向mmsghdr结构数组的指针。该数组的大小在vlen中指定。
mmsghdr结构定义为:
struct mmsghdr {
struct msghdr msg_hdr; /* Message header */
unsigned int msg_len; /* Number of received bytes for header */
};
msg_hdr字段是msghdr结构,如recvmsg(2)中所述。 msg_len字段是为条目中的消息返回的字节数。该字段的值与标头上单个recvmsg(2)的返回值相同。
flags参数包含在一起进行或运算的标志。这些标志与为recvmsg(2)记录的标志相同,并增加了以下内容:
- MSG_WAITFORONE(since Linux 2.6.34)
- 收到第一条消息后,打开MSG_DONTWAIT。
timeout参数指向为接收操作定义超时(秒加纳秒)的结构timespec(请参阅clock_gettime(2))(但请参阅BUGS!)。 (此间隔将四舍五入为系统时钟的粒度,并且内核调度延迟意味着阻塞间隔可能会少量溢出。)如果超时为NULL,则操作将无限期阻塞。
阻塞的recvmmsg()调用会阻塞,直到收到vlen消息或超时到期为止。无阻塞呼叫将读取尽可能多的消息(达到vlen指定的限制)并立即返回。
从recvmmsg()返回时,msgvec的连续元素将更新为包含有关每个接收到的消息的信息:msg_len包含接收到的消息的大小; msg_hdr的子字段按照recvmsg(2)中的描述进行更新。调用的返回值指示已更新的msgvec元素的数量。
返回值
成功时,recvmmsg()返回msgvec中收到的消息数;如果出错,则返回-1,并且将errno设置为指示错误。
版本
在Linux 2.6.33中添加了recvmmsg()系统调用。在glibc中的支持已在2.12版中添加。
遵循规范
recvmmsg()是特定于Linux的。
BUGS
超时参数不能按预期工作。仅在收到每个数据报之后才检查超时,因此,如果在超时到期之前最多接收到vlen-1个数据报,但是又没有收到其他数据报,则该呼叫将永远阻塞。
如果至少收到一条消息后发生错误,则调用成功,并返回收到的消息数。该错误代码预计将在对recvmmsg()的后续调用中返回。但是,在当前的实现中,与此同时,错误代码可能会被套接字上不相关的网络事件(例如传入的ICMP数据包)覆盖。
示例
以下程序使用recvmmsg()在套接字上接收多个消息,并将它们存储在多个缓冲区中。如果所有缓冲区都已满,或者指定的超时时间已到,则调用返回。
以下代码段定期生成包含随机数的UDP数据报:
$ while true; do echo $RANDOM > /dev/udp/127.0.0.1/1234;
sleep 0.25; done
这些数据报由示例应用程序读取,可以提供以下输出:
$ ./a.out 5 messages received 1 11782 2 11345 3 304 4 13514 5 28421
Program source
#define _GNU_SOURCE
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
int
main(void)
{
#define VLEN 10
#define BUFSIZE 200
#define TIMEOUT 1
int sockfd, retval, i;
struct sockaddr_in addr;
struct mmsghdr msgs[VLEN];
struct iovec iovecs[VLEN];
char bufs[VLEN][BUFSIZE+1];
struct timespec timeout;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket()");
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(1234);
if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
perror("bind()");
exit(EXIT_FAILURE);
}
memset(msgs, 0, sizeof(msgs));
for (i = 0; i < VLEN; i++) {
iovecs[i].iov_base = bufs[i];
iovecs[i].iov_len = BUFSIZE;
msgs[i].msg_hdr.msg_iov = &iovecs[i];
msgs[i].msg_hdr.msg_iovlen = 1;
}
timeout.tv_sec = TIMEOUT;
timeout.tv_nsec = 0;
retval = recvmmsg(sockfd, msgs, VLEN, 0, &timeout);
if (retval == -1) {
perror("recvmmsg()");
exit(EXIT_FAILURE);
}
printf("%d messages received\n", retval);
for (i = 0; i < retval; i++) {
bufs[i][msgs[i].msg_len] = 0;
printf("%d %s", i+1, bufs[i]);
}
exit(EXIT_SUCCESS);
}
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。

