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/。