#define _GNU_SOURCE /* feature_test_macros(7) 参照 */ #include <sys/socket.h> int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
sockfd 引数は、データを受信するソケットのファイルディスクリプターである。
msgvec 引数は mmsghdr 構造体の配列である。 この配列の大きさは vlen で指定する。
mmsghdr 構造体は <sys/socket.h> で次のように定義されている。
struct mmsghdr {
struct msghdr msg_hdr; /* メッセージヘッダー */
unsigned int msg_len; /* このヘッダーで受信されたバイト数 */
};
msg_hdr フィールドは、 recvmsg(2) で説明されている msghdr 構造体である。 msg_len フィールドは、 このエントリーで返されるメッセージのバイト数で、 このヘッダーに対して recvmsg(2) を呼び出した場合の返り値と同じ値が入る。
flags 引数には複数のフラグを論理和 (OR) で指定できる。 フラグは、 recvmsg(2) で説明されているものに加えて、以下が使用できる。
timeout 引数は struct timespec (clock_gettime(2) 参照) へのポインターで、 この構造体で受信操作のタイムアウト (秒とナノ秒) を指定する (ただし、バグを参照のこと) (待ち時間はシステムクロックの粒度に切り上げられ、カーネルのスケジューリング遅延により少しだけ長くなる可能性がある)。 timeoutが NULL の場合、 受信操作は無期限に停止 (block) する。
停止 (blocking) モードの recvmmsg() の呼び出しは、 vlen 個のメッセージを受信するか、タイムアウトが満了するまで停止する。 非停止 (nonblocking) モードの呼び出しでは、 読み出し可能なメッセージ (最大で vlen 個) を読み出し、 すぐに返る。
recvmmsg() が返った際には、 msgvec のうちデータが受信された要素には、受信したそれぞれのメッセージの情報が格納されている。 また、 msg_len には受信したメッセージの大きさが入り、 msg_hdr の各フィールドは recvmsg(2) に書かれている通りに更新される。 呼び出しの返り値は、更新された msgvec の要素数である。
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(). In the current implementation, however, the error code can be overwritten in the meantime by an unrelated network event on a socket, for example an incoming ICMP packet.
以下のコマンドは、 ランダムな数字が入った 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
int
main(void)
{
#define VLEN 10
#define BUFSIZE 200
#define TIMEOUT 1
int sockfd, retval;
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 (int 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 (int i = 0; i < retval; i++) {
bufs[i][msgs[i].msg_len] = 0;
printf("%d %s", i+1, bufs[i]);
}
exit(EXIT_SUCCESS);
}