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 of mmsghdr structures. the size of that array is specified in n.

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_hdr field is a msghdr structure, as described over in recvmsg(2).
  • the msg_len field is the number of bytes returned for the message in that entry. it holds the exact same value as the return value of a single recvmsg(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 on MSG_DONTWAIT after 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 until n messages 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 in msgvec.
  • on error: -1 is returned, and errno is set to indicate the error.

πŸ’€ ERRORS πŸ’€

errors are as for recvmsg(2). plus this one extra:

  • EINVAL β€” timeout is 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: the timeout argument does not work as intended. the timeout is checked only after the receipt of each datagram, so if up to n-1 datagrams 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
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms