BSDソケットインターフェースの話ですが。

ファイルディスクリプタをノンブロッキングに設定すると、読めるデータがない(受信バッファにデータがない)場合は read() / recv() から -1 が返ってきて、errno が EAGAIN に設定されます。一方これとは別に、ソケットに対して recv() する場合、第4引数(フラグ)に MSG_WAITALL を指定すると、第3引数で指定した分だけ読めるまで返ってきません(基本的には)。

じゃあ両方やるとどうなるの?というわけで以下のテストプログラム。

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>

int main(int argc, char **argv)
{
        struct addrinfo *ai;
        int fd;
        int on = 1;
        char buf[1000];
        ssize_t n;

        getaddrinfo(argv[1], argv[2], NULL, &ai);
        fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        ioctl(fd, FIONBIO, &on);
        connect(fd, ai->ai_addr, ai->ai_addrlen);
        sleep(1);
        errno = 0;
        n = recv(fd, buf, 1000, MSG_WAITALL);
        printf("ret=%d errno=%d %s\n", n, errno, strerror(errno));

        return 0;
}

これで適当なサーバにつないで動作を見ます。sleep() が挟まっているのは、受信バッファにデータが溜まるのを待つためです。SMTPサーバはサーバから話し始めるので、データが溜まり、HTTPサーバはクライアントから話し始めるのでデータは溜まりません。

まず CentOS 5 (kernel-2.6.18-194.32.1.el5) での実行例。

[umezawa@nyx:pts/1 ~]$ ./recv_nonblocking_waitall localhost 25
ret=29 errno=0 Success
[umezawa@nyx:pts/1 ~]$ ./recv_nonblocking_waitall localhost 80
ret=-1 errno=11 Resource temporarily unavailable
[umezawa@nyx:pts/1 ~]$

どうやらノンブロッキング指定のほうが「一方的に強い」ようで、MSG_WAITALL が指定されていないのと同じ挙動になります。

一方、FreeBSD 8.1-RELEASE では

[umezawa@devfreebsd ~]$ ./recv_nonblocking_waitall localhost 25
ret=-1 errno=35 Resource temporarily unavailable
[umezawa@devfreebsd ~]$ ./recv_nonblocking_waitall nyx.local 80
ret=-1 errno=35 Resource temporarily unavailable
[umezawa@devfreebsd ~]$

となり、ノンブロッキングの指定と MSG_WAITALL の指定が矛盾なく組み合わさっている…つまり、指定した分だけ読めない場合は即座に EAGAIN で失敗するように見えます。

結論としては「移植性がないので使わないほうがいい」というところに落ち着きそうです。以上、誰得情報でした(ぁ

Trackback

no comment untill now

Add your comment now