Provided by: manpages-ja-dev_0.5.0.0.20221215+dfsg-1_all bug

名前

       recvmmsg - 複数のメッセージをソケットから受信する

書式

       #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);

説明

       recvmmsg()  システムコールは recvmsg(2) の拡張で、 このシステムコールを使うと一度の呼び出しでソケットから
       複数のメッセージを受信することができる     (アプリケーションによっては性能上のメリットがある)。      他に
       recvmsg(2) から拡張されている点としては、受信操作におけるタイムアウトのサポートがある。

       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) で説明されているものに加え
       て、以下が使用できる。

       MSG_WAITFORONE (Linux 2.6.34 以降)
              最初のメッセージを受信後に MSG_DONTWAIT を有効にする。

       timeout 引数は struct timespec (clock_gettime(2) 参照) へのポインターで、  この構造体で受信操作のタイムア
       ウト  (秒とナノ秒)  を指定する  (ただし、バグを参照のこと)  (待ち時間はシステムクロックの粒度に切り上げら
       れ、カーネルのスケジューリング遅延により少しだけ長くなる可能性がある)。 timeoutが NULL の場合、  受信操作
       は無期限に停止 (block) する。

       停止  (blocking) モードの recvmmsg() の呼び出しは、 vlen 個のメッセージを受信するか、タイムアウトが満了す
       るまで停止する。 非停止 (nonblocking) モードの呼び出しでは、 読み出し可能なメッセージ (最大で vlen 個) を
       読み出し、 すぐに返る。

       recvmmsg() が返った際には、 msgvec のうちデータが受信された要素には、受信したそれぞれのメッセージの情報が
       格納されている。   また、   msg_len   には受信したメッセージの大きさが入り、   msg_hdr   の各フィールドは
       recvmsg(2) に書かれている通りに更新される。 呼び出しの返り値は、更新された msgvec の要素数である。

返り値

       成功すると、  recvmmsg() は msgvec に受信されたメッセージ数を返す。 エラーの場合、 -1 を返し、 errno にエ
       ラーを示す値を設定する。

エラー

       エラーは recvmsg(2) と同じである。 これに加えて、以下のエラーが起こる場合がある。

       EINVAL timeout が無効である。

       See also BUGS.

バージョン

       recvmmsg() システムコールは Linux 2.6.33 で追加された。 glibc でのサポートはバージョン 2.12  以降で利用可
       能である。

準拠

       recvmmsg() は Linux 固有である。

バグ

       timeout  引数は意図した通りには動作しない。 タイムアウトは各データグラムの受信後にのみチェックされる。 そ
       のため、 タイムアウトが満了する前に  vlen-1  個のデータグラムを受信し、  その後全くデータグラムを受信しな
       かった場合、 呼び出しはずっと停止し続けてしまう。

       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.

       以下のプログラムは、 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

   プログラムのソース

       #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;
           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);
       }

関連項目

       clock_gettime(2), recvmsg(2), sendmmsg(2), sendmsg(2), socket(2), socket(7)

この文書について

       この man ページは Linux man-pages プロジェクトのリリース 5.10  の一部である。プロジェクトの説明とバグ報告
       に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。