From: Takahiro Kambe Date: 2003-08-19T01:44:54+09:00 Subject: [ruby-list:38139] Re: ポートが閉じているときの例外など In message <200308180959.h7I9xnb7001977@sharui.nakada.kanuma.tochigi.jp> on Mon, 18 Aug 2003 18:59:50 +0900, nobu.nakada@nifty.ne.jp wrote: > > (必須ではないかもしれませんが)EINPRROGRESSのときもやった方が良いと言え > > ます。早くエラーが検知できて、connect(2)の呼び出しを1回節約できる場合 > > があります。 > > EINPROGRESSのような時間がかかっているときに、システムコール一回 > の間くらいではそうそう条件は変わらないのではないかという気がす > るのですが。 手元のテストでは、 1. connect()失敗 (EINPROGRESS) 2. getsockopt()で ECONNREFUSED を検知 といったことが確認できています。相手がlocalhostだと、同一ホスト内なの でEINPROGRESSに至る間もなく、ECONNREFUSEDであることがわかります。同一 LAN上のホストを相手にすると、EINPROGRESSの場合にgetsockopt(2)でチェッ クしない場合は、 1. connect()失敗 (EINPROGRESS) 2. connect()失敗 (EINVAL) 3. getsockopt()で ECONNREFUSED を検知 となります。 CPUが高速、メモリやハードディスクがいっぱいになっても、システムコール にかかるオーバーヘッドは、(システムコールの呼び出しを伴わない)ライブラ リに比べるとはるかに大きいものです。必要のないシステムコールの呼び出し をやめることができることができれば、それに越したことはありません。 > 対応としては、FreeBSD(*BSD全部?)でもWAIT_IN_PROGRESSを有効にす > るということでいいんでしょうか。全システムで有効にしたほうがい > いのかな? 以下の環境の確認はできました。 NetBSD 1.6 and current FreeBSD 2.2.8 OpenBSD 3.3 FreeBSDの最新のものはチェックできません。 http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=22522 に含めたテストプログラムを後ろに付けます。PR(Problem Report)にも書きま したが、connect(2)でEINVALになる場合は、 % cc test.c % ./a.out 192.168.32.10 12345 connect to family = 2, port = 234, addr = 192.168.32.10 connect: Operation now in progress getsockopt => Connection refused connect to family = 2, port = 234, addr = 192.168.32.10 connect: Invalid argument getsockopt => 0 となります。 ===================================================================== #include #include #include #include #include #include #include #include #include #include #include static int connect_test(int, struct sockaddr_in *, int, int); #define GETSOCKOPT_AFTER_EINPROGRESS 1 #define GETSOCKOPT_AFTER_EINVAL 1 static int exit_after_getsockopt = 0; int main(int argc, char **argv) { char *host, *port; struct hostent *ht; unsigned short p; int fd, status, n; struct sockaddr_in sin; if (argc == 3) { host = argv[1]; port = argv[2]; } else { fputs("Need host and port as parameter.\n", stderr); exit(1); } n = atoi(port); if (n <= 0 || n > 65535) { fputs("Invalid port number.\n", stderr); exit(1); } p = n; ht = gethostbyname(host); if (ht == NULL) { herror(host); } memset(&sin, '\0', sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(p); memcpy(&sin.sin_addr, ht->h_addr_list[0], ht->h_length); fd = socket(PF_INET, SOCK_STREAM, 0); status = connect_test(fd, &sin, sizeof sin, 0); return(status); } static int connect_test(fd, sockaddr, len, socks) int fd; struct sockaddr_in *sockaddr; int len; int socks; { int status; int mode; #if (GETSOCKOPT_AFTER_EINPROGRESS == 1) || (GETSOCKOPT_AFTER_EINVAL == 1) int value; #endif mode = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, mode | O_NONBLOCK); for (;;) { printf("connect to family = %d, port = %d, addr = %s\n", sockaddr->sin_family, ntohs(sockaddr->sin_port), inet_ntoa(sockaddr->sin_addr)); status = connect(fd, (struct sockaddr *)sockaddr, len); if (status < 0) { perror("connect"); switch (errno) { case EAGAIN: case EINPROGRESS: #if (GETSOCKOPT_AFTER_EINPROGRESS == 1) len = sizeof(value); getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &len); printf("getsockopt => %s\n", (value != 0) ? strerror(value): "0"); if (exit_after_getsockopt) goto error; #endif continue; case EISCONN: status = 0; break; case EINVAL: #if (GETSOCKOPT_AFTER_EINVAL == 1) len = sizeof(value); getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &len); printf("getsockopt => %s\n", (value != 0) ? strerror(value): "0"); if (exit_after_getsockopt) goto error; #endif break; default: break; } } #if (GETSOCKOPT_AFTER_EINPROGRESS == 1) || (GETSOCKOPT_AFTER_EINVAL == 1) error: ; #endif fcntl(fd, F_SETFL, mode); return status; } } ===================================================================== -- 神戸 隆博 / Takahiro Kambe