recvmmsg(2)
π recvmmsg(2) β the manual page, but make it make sense π
okay bestie, welcome to the System Calls Manual entry for recvmmsg(2) β one of those Linux-only little gems that lives in section 2 of the manual. i gotchu, we're gonna get through this together π«Ά
β¨ NAME β¨
recvmmsg β receive multiple messages on a socket π
π LIBRARY π
Standard C library (libc, -lc) β yeah she's just chilling in libc, no extra linking drama πΈ
π SYNOPSIS π
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/socket.h>
int recvmmsg(unsigned int n;
int sockfd, struct mmsghdr msgvec[n], unsigned int n,
int flags, struct timespec *timeout);
(that's literally how it's printed, bestie β don't come for me, come for the man page π)
π¦ DESCRIPTION π¦
so. recvmmsg() is the system call that's basically recvmsg(2) but β¨extraβ¨ β it lets the caller receive multiple messages from a socket with one single system call. it's giving efficiency, it's giving main character energy. (the man page notes this has performance benefits for some applications, and honestly? valid.)
and there's a second glow-up over recvmsg(2): it supports a timeout on the receive operation. iconic. π
now the cast of characters:
sockfdβ the file descriptor of the socket you're pulling data out of. that's the socket you're receiving from, periodt.msgvecβ a pointer to an array ofmmsghdrstructures. the size of that array is specified inn.
the mmsghdr structure is defined in <sys/socket.h> like this:
struct mmsghdr {
struct msghdr msg_hdr; /* Message header */
unsigned int msg_len; /* Number of received bytes for header */
};
- the
msg_hdrfield is amsghdrstructure, as described over inrecvmsg(2). - the
msg_lenfield is the number of bytes returned for the message in that entry. it holds the exact same value as the return value of a singlerecvmsg(2)on that header.
the flags argument is flags ORed together, and they're the same ones documented for recvmsg(2) β with one extra addition:
MSG_WAITFORONE(since Linux 2.6.34) β turns onMSG_DONTWAITafter the first message has been received. π
the timeout argument points to a struct timespec (see clock_gettime(2)) that defines a timeout β seconds plus nanoseconds β for the receive operation (but see BUGS!). heads up: this interval gets rounded up to the system clock granularity, and kernel scheduling delays mean the blocking interval may overrun by a small amount. if timeout is NULL, then the operation just blocks indefinitely. no timer, no escape, just vibes π
now, behavior:
- a blocking
recvmmsg()call blocks untilnmessages have been received, or until the timeout expires. - a nonblocking call reads as many messages as are available (up to the limit specified by
n) and returns immediately. snappy π
on return from recvmmsg(), successive elements of msgvec are updated to contain the info about each received message: msg_len contains the size of the received message, and the subfields of msg_hdr are updated as described in recvmsg(2). the return value of the call tells you how many elements of msgvec got updated π
π RETURN VALUE π
- on success:
recvmmsg()returns the number of messages received inmsgvec. - on error:
-1is returned, anderrnois set to indicate the error.
π ERRORS π
errors are as for recvmsg(2). plus this one extra:
EINVALβtimeoutis invalid. π
also, see BUGS. yeah. see BUGS. π
π STANDARDS π
Linux. that's it. that's the standard. π
π HISTORY π
Linux 2.6.33, glibc 2.12. she's been around. she's seen things. π
β οΈ BUGS β οΈ
okay bestie, sit down, we need to talk. two whole bugs.
bug one: thetimeoutargument does not work as intended. the timeout is checked only after the receipt of each datagram, so if up ton-1datagrams are received before the timeout expires, but then no further datagrams are received, the call will block forever. like, forever forever. π
bug two: if an error occurs after at least one message has been received, the call succeeds and returns the number of messages received. the error code is expected to be returned on a subsequent call to recvmmsg(). however, in the current implementation, the error code can be overwritten in the meantime by an unrelated network event on a socket β for example an incoming ICMP packet. so your error can just... get yoinked. π
πΈ EXAMPLES πΈ
the following program uses recvmmsg() to receive multiple messages on a socket and stores them in multiple buffers. the call returns if all buffers are filled, or if the timeout specified has expired.
first, this snippet periodically generates UDP datagrams containing a random number:
$ while true; do echo $RANDOM > /dev/udp/127.0.0.1/1234;
sleep 0.25; done
these datagrams are read by the example application, which can give the following output:
$ ./a.out
5 messages received
1 11782
2 11345
3 304
4 13514
5 28421
π§ Program source π§
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
int
main(void)
{
#define VLEN 10
#define BUFSIZE 200
#define TIMEOUT 1
int sockfd, retval;
char bufs[VLEN][BUFSIZE+1];
struct iovec iovecs[VLEN];
struct mmsghdr msgs[VLEN];
struct timespec timeout;
struct sockaddr_in addr;
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 (size_t 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 (size_t i = 0; i < retval; i++) {
bufs[i][msgs[i].msg_len] = 0;
printf("%zu %s", i+1, bufs[i]);
}
exit(EXIT_SUCCESS);
}
π SEE ALSO π
clock_gettime(2), recvmsg(2), sendmmsg(2), sendmsg(2), socket(2), socket(7)
and that's the whole page, bestie!! π this was recvmmsg(2) from Linux man-pages 6.20, dated 2026-02-08. go forth and receive multiple messages with one syscall, you absolute icon πβ¨π¦
π¦ where this page comes from
- arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- π the page's license: Linux-man-pages-copyleft
- Copyright 2011, Andi Kleen <andi@firstfloor.org>
- Copyright 2011, Michael Kerrisk <mtk.manpages@gmail.com>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms