OSDN Git Service

(split) Convert release and draft pages to UTF-8.
[linuxjm/LDP_man-pages.git] / release / man2 / select_tut.2
1 .\" This manpage is copyright (C) 2001 Paul Sheer.
2 .\"
3 .\" Permission is granted to make and distribute verbatim copies of this
4 .\" manual provided the copyright notice and this permission notice are
5 .\" preserved on all copies.
6 .\"
7 .\" Permission is granted to copy and distribute modified versions of this
8 .\" manual under the conditions for verbatim copying, provided that the
9 .\" entire resulting derived work is distributed under the terms of a
10 .\" permission notice identical to this one.
11 .\"
12 .\" Since the Linux kernel and libraries are constantly changing, this
13 .\" manual page may be incorrect or out-of-date.  The author(s) assume no
14 .\" responsibility for errors or omissions, or for damages resulting from
15 .\" the use of the information contained herein.  The author(s) may not
16 .\" have taken the same level of care in the production of this manual,
17 .\" which is licensed free of charge, as they might when working
18 .\" professionally.
19 .\"
20 .\" Formatted or processed versions of this manual, if unaccompanied by
21 .\" the source, must acknowledge the copyright and authors of this work.
22 .\"
23 .\" very minor changes, aeb
24 .\"
25 .\" Modified 5 June 2002, Michael Kerrisk <mtk.manpages@gmail.com>
26 .\" 2006-05-13, mtk, removed much material that is redundant with select.2
27 .\"             various other changes
28 .\" 2008-01-26, mtk, substantial changes and rewrites
29 .\"
30 .\" Translated 2002-02-21, NAKANO Takeo <nakano@apm.seikei.ac.jp>
31 .\" Updated 2002-09-23, Akihiro MOTOKI <amotoki@dd.iij4u.or.jp>
32 .\" Updated 2005-03-15, Akihiro MOTOKI <amotoki@dd.iij4u.or.jp>
33 .\" Updated 2006-07-21, Akihiro MOTOKI, LDP v2.36
34 .\" Updated 2009-02-13, Akihiro MOTOKI, LDP v3.18
35 .\"
36 .\"WORD:        file descriptor         ファイルディスクリプタ
37 .\"WORD:        exceptional condition   例外状態
38 .\"
39 .TH SELECT_TUT 2 2010-06-10 "Linux" "Linux Programmer's Manual"
40 .SH 名前
41 select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO \- 同期 I/O の多重化
42 .SH 書式
43 .nf
44 /* POSIX.1-2001 に従う場合 */
45 .br
46 .B #include <sys/select.h>
47 .sp
48 /* 以前の規格に従う場合 */
49 .br
50 .B #include <sys/time.h>
51 .br
52 .B #include <sys/types.h>
53 .br
54 .B #include <unistd.h>
55 .sp
56 .BI "int select(int " nfds ", fd_set *" readfds ", fd_set *" writefds ,
57 .BI "           fd_set *" exceptfds ", struct timeval *" utimeout );
58 .sp
59 .BI "void FD_CLR(int " fd ", fd_set *" set );
60 .br
61 .BI "int  FD_ISSET(int " fd ", fd_set *" set );
62 .br
63 .BI "void FD_SET(int " fd ", fd_set *" set );
64 .br
65 .BI "void FD_ZERO(fd_set *" set );
66 .sp
67 .B #include <sys/select.h>
68 .sp
69 .BI "int pselect(int " nfds ", fd_set *" readfds ", fd_set *" writefds ,
70 .BI "            fd_set *" exceptfds ", const struct timespec *" ntimeout ,
71 .BI "            const sigset_t *" sigmask );
72 .fi
73 .sp
74 .in -4n
75 glibc 向けの機能検査マクロの要件
76 .RB ( feature_test_macros (7)
77 参照):
78 .in
79 .sp
80 .BR pselect ():
81 _POSIX_C_SOURCE\ >=\ 200112L || _XOPEN_SOURCE\ >=\ 600
82 .SH 説明
83 .BR select ()
84 (や
85 .BR pselect ())
86 を使うと、効率的に複数のファイルディスクリプタを監視し、
87 そのファイルディスクリプタのいずれかが
88 「ready (準備ができた)」状態、つまり I/O (入出力) が可能になっているかや、
89 ファイルディスクリプタのいずれかが
90 「例外状態 (exceptional condition)」が発生したか、を調べることができる。
91
92 この関数の主要な引き数は、3種類のファイルディスクリプタの「集合」
93 \fIreadfds\fP, \fIwritefds\fP, \fIexceptfds\fP である。
94 各々の集合は
95 .B fd_set
96 として宣言され、その内容は
97 .BR FD_CLR (),
98 .BR FD_ISSET (),
99 .BR FD_SET (),
100 .BR FD_ZERO ()
101 といったマクロによって操作できる。
102 新しく宣言された集合は、まず最初に
103 .BR FD_ZERO ()
104 を使ってクリアすべきである。
105 .BR select ()
106 はこれらの集合の内容を、以降に述べる規則に従って修正する。
107 .BR select ()
108 を呼んだ後、ファイルディスクリプタがまだ集合に存在しているかどうかは、
109 .BR FD_ISSET ()
110 マクロによって調べることができる。
111 .BR FD_ISSET ()
112 は指定されたディスクリプタが集合に存在していれば 0 以外の値を返し、
113 存在しなければ 0 を返す。
114 .BR FD_CLR ()
115 は集合からのファイルディスクリプタの削除を行う。
116 .SS 引き数
117 .TP
118 \fIreadfds\fP
119 この集合に含まれるいずれかのファイルディスクリプタで、
120 データの読み込みが可能になったかどうかを監視する。
121 .BR select ()
122 から戻る時に、\fIreadfds\fP のうち、
123 直ちに読み込み可能なファイルディスクリプタ以外は
124 集合から削除される。
125 .TP
126 \fIwritefds\fP
127 この集合に含まれるいずれかのファイルディスクリプタで、
128 データを書き込むスペースがあるかどうかを監視する。
129 .BR select ()
130 から戻る時に、\fIwritefds\fP のうち、
131 直ちに書き込み可能なファイルディスクリプタ以外は
132 集合から削除される。
133 .TP
134 \fIexceptfds\fP
135 この集合に含まれるいずれかのファイルディスクリプタで、
136 「例外状態 (exceptional condition)」が発生したかどうかを監視する。
137 実際の動作では、普通に起こり得る例外状態は一つだけであり、
138 それは TCP ソケットで \fI帯域外 (out-of-band; OOB)\fP データが
139 読み込み可能な場合である。
140 OOB データの詳細については、
141 .BR recv (2),
142 .BR send (2),
143 .BR tcp (7)
144 を参照のこと。
145 (これ以外では、まれなことだが、
146 パケットモードの擬似端末 (pseudoterminals) で
147 .BR select ()
148 が例外状態を示すことがある。)
149 .BR select ()
150 が返る時に、\fIexceptfds\fP のうち、
151 例外状態が発生したディスクリプタ以外は集合から削除される。
152 .TP
153 \fInfds\fP
154 全ての集合に含まれるファイルディスクリプタのうち、
155 値が最大のものに 1 を足した整数である。
156 すなわち、ファイルディスクリプタを各集合に加える作業の途中で、
157 全てのファイルディスクリプタを見て最大値を求め、
158 それに 1 を加えて \fInfds\fP として渡さないといけない、ということだ。
159 .TP
160 \fIutimeout\fP
161 (何も起こらなかった場合に)
162 .BR select ()
163 が戻る前に待つ最大時間である。
164 この値に NULL を渡すと、
165 .BR select ()
166 はファイルディスクリプタのいずれかが ready (準備ができた) 状態に
167 なるまで待ち続けてずっと停止する。
168 \fIutimeout\fP は 0 秒にすることもでき、
169 この場合
170 .BR select ()
171 は直ちに返り、呼び出し時点のファイルディスクリプタの状態に
172 関する情報が返される。
173 構造体 \fIstruct timeval\fP は次のように定義されている:
174 .IP
175 .in +4n
176 .nf
177 struct timeval {
178     time_t tv_sec;    /* seconds */
179     long tv_usec;     /* microseconds */
180 };
181 .fi
182 .in
183 .TP
184 \fIntimeout\fP
185 .BR pselect ()
186 のこの引き数は
187 .I utimeout
188 と同じ意味を持つが、
189 .I "struct timespec"
190 は次に示すようにナノ秒の精度を持つ。
191 .in +4n
192 .nf
193 struct timespec {
194     long tv_sec;    /* seconds */
195     long tv_nsec;   /* nanoseconds */
196 };
197 .fi
198 .in
199 .TP
200 \fIsigmask\fP
201 この引き数は、呼び出し側が
202 .BR pselect ()
203 内部で停止している間に、カーネルが通知を許可すべきシグナル集合
204 (すなわち、呼び出したスレッドのシグナルマスクから削除すべきシグナル集合)
205 を保持する
206 .RB ( sigaddset (3)
207
208 .BR sigprocmask (2)
209 を参照)。
210 この引き数は NULL にすることもでき、その場合はこの関数へ
211 入るとき・出るときにシグナルマスクを変更しない。
212 この場合、
213 .BR pselect ()
214
215 .BR select ()
216 と全く同じ動作となる。
217 .SS シグナルとデータイベントを組み合わせる
218 ファイルディスクリプタが I/O 可能な状態になるのと同時に
219 シグナルも待ちたい場合には、
220 .BR pselect ()
221 が便利である。
222 シグナルを受信するプログラムは、通常は、
223 シグナルハンドラをグローバルなフラグを立てるためだけに使う。
224 このグローバルなフラグは、
225 そのイベントをプログラムのメインループで
226 処理しなければならないことを示す。
227 シグナルを受けると
228 .BR select ()
229 (や
230 .BR pselect ())
231
232 \fIerrno\fP に \fBEINTR\fP をセットして戻ることになる。
233 シグナルがプログラムのメインループで処理されるためにはこの動作が不可欠で、
234 これがないと
235 .BR select ()
236 は永遠に停止し続けることになる。
237 さて、メインループのどこかにこのグローバルフラグをチェックする
238 条件文があるとしよう。ここで少し考えてみないといけない。
239 「シグナルが条件文の後、しかし
240 .BR select ()
241 コールの前に到着したら
242 どうなるのか?」
243 答えは「その
244 .BR select ()
245 は、たとえ解決待ちのイベントがあったとしても、
246 永遠に停止する」である。
247 この競合状態は
248 .BR pselect ()
249 コールによって解決できる。
250 このコールを使うと、
251 .BR pselect ()
252 で受信したいシグナルの集合だけをシグナルマスクに設定することができる。
253 例えば、問題となっているイベントが子プロセスの終了の場合を考えよう。
254 メインループが始まる前に、
255 .B SIGCHLD
256
257 .BR sigprocmask (2)
258 でブロックする。
259 .BR pselect ()
260 コールでは
261 .B SIGCHLD
262 を、もともとのシグナルマスクを使って有効にするのだ。
263 このプログラムは次のようになる。
264 .PP
265 .nf
266 static volatile sig_atomic_t got_SIGCHLD = 0;
267
268 static void
269 child_sig_handler(int sig)
270 {
271     got_SIGCHLD = 1;
272 }
273
274 int
275 main(int argc, char *argv[])
276 {
277     sigset_t sigmask, empty_mask;
278     struct sigaction sa;
279     fd_set readfds, writefds, exceptfds;
280     int r;
281
282     sigemptyset(&sigmask);
283     sigaddset(&sigmask, SIGCHLD);
284     if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == \-1) {
285         perror("sigprocmask");
286         exit(EXIT_FAILURE);
287     }
288
289     sa.sa_flags = 0;
290     sa.sa_handler = child_sig_handler;
291     sigemptyset(&sa.sa_mask);
292     if (sigaction(SIGCHLD, &sa, NULL) == \-1) {
293         perror("sigaction");
294         exit(EXIT_FAILURE);
295     }
296
297     sigemptyset(&empty_mask);
298
299     for (;;) {          /* main loop */
300         /* Initialize readfds, writefds, and exceptfds
301            before the pselect() call. (Code omitted.) */
302
303         r = pselect(nfds, &readfds, &writefds, &exceptfds,
304                     NULL, &empty_mask);
305         if (r == \-1 && errno != EINTR) {
306             /* Handle error */
307         }
308
309         if (got_SIGCHLD) {
310             got_SIGCHLD = 0;
311
312             /* Handle signalled event here; e.g., wait() for all
313                terminated children. (Code omitted.) */
314         }
315
316         /* main body of program */
317     }
318 }
319 .fi
320 .SS 実例
321 実際のところ
322 .BR select ()
323 の大事な点は何なのか?
324 ディスクリプタは好きなときに読み書きできるんじゃないの?
325 .BR select ()
326 の重要なところは、複数のディスクリプタを同時に監視でき、
327 なんの動きもなければプロセスを適切にスリープ状態に移行するところにあるのだ。
328 UNIX プログラマは、
329 複数のファイルディスクリプタの入出力を同時に扱わねばならず、
330 しかもデータの流れは間欠的である、という状況によく出会う。
331 単に
332 .BR read (2)
333
334 .BR write (2)
335 コールのシーケンスを作るだけでは、それらのコールのどれかが
336 ファイルディスクリプタからのデータを待ってブロックしており、
337 別のファイルディスクリプタには I/O が可能なのに使えない、
338 ということになってしまうだろう。
339 .BR select ()
340 を使うとこの状況に効果的に対処できる。
341 .SS SELECT の掟
342 .BR select ()
343 を使おうとした多くの人は、理解しにくい挙動に出くわし、結果的に
344 できたものは移植性がないか、よくてもギリギリのものになってしまう。
345 例えば、上記のプログラムは、
346 集合に含まれるファイルディスクリプタを非停止 (nonblocking) モード
347 にしなくても、どこにもブロックが生じないよう注意して書かれている。
348 微妙な間違いによって、
349 .BR select ()
350 を使う利点は簡単に失われてしまう。
351 そこで、
352 .BR select ()
353 コールを使うときに注意すべき重要事項を列挙しておくことにする。
354 .TP 4
355 1.
356 .BR select ()
357 を使うときは、タイムアウトは設定すべきでない。
358 処理するデータが無いときには、
359 あなたのプログラムには何もすることは無いはずである。
360 タイムアウトに依存したコードは通常移植性がなく、
361 デバッグも難しくなる。
362 .TP
363 2.
364 上述したように、
365 効率的なプログラムを書くには
366 .I nfds
367 の値を適切に計算して与えなければならない。
368 .TP
369 3.
370 .BR select ()
371 コールの終了後に結果をチェックして、
372 適切に対応するつもりのないファイルディスクリプタは、
373 どの集合にも加えてはならない。
374 次のルールも参照。
375 .TP
376 4.
377 .BR select ()
378 から返った後には、全ての集合の全てのファイルディスクリプタについて
379 読み書き可能な状態になっているかをチェックすべきである。
380 .TP
381 5.
382 .BR read (2),
383 .BR recv (2),
384 .BR write (2),
385 .BR send (2)
386 といった関数は、こちらが要求した全データを読み書きする必要は\fIない\fP。
387 もし全データを読み書きするなら、それはトラフィックの負荷が小さく、
388 ストリームが速い場合だろう。この条件は常に満たされるとは限らない。
389 これらの関数が頑張っても 1 バイトしか送受信できないような場合も
390 考慮に入れてやらなければならない。
391 .TP
392 6.
393 処理するデータ量が小さいことがはっきりとわかっている場合を除いて、
394 一度に 1 バイトずつ読み書きするようなことはしてはならない。
395 バッファの許すかぎりのデータをまとめて読み書きしないと、
396 非常に効率が悪い。下記の例ではバッファは 1024 バイトにしているが、
397 このサイズを大きくするのは簡単だろう。
398 .TP
399 7.
400 .BR read (2),
401 .BR recv (2),
402 .BR write (2),
403 .BR send (2)
404 などの関数や
405 .BR select ()
406 コールは、
407 .I errno
408
409 .B EINTR " や " EAGAIN
410 .RB ( EWOULDBLOCK )
411 にして \-1 を返すことがある。
412 このような結果に対して適切に対応してやらなければならない
413 (上記の例ではしていない)。
414 書いているプログラムがシグナルを受ける予定がなければ、
415 .B EINTR
416 が返されることはあまり考えられない。
417 書いているプログラムで非ブロック I/O をセットしていない場合は、
418 .B EAGAIN
419 が返されることはないだろう。
420 .\" それでもなお、完全を期するならば、
421 .\" これらのエラーを考慮に入れる必要がある。
422 .TP
423 8.
424 決して、引き数に長さ 0 のバッファを指定して
425 .BR read (2),
426 .BR recv (2),
427 .BR write (2),
428 .BR send (2)
429 を呼び出してはならない。
430 .TP
431 9.
432 .BR read (2),
433 .BR recv (2),
434 .BR write (2),
435 .BR send (2)
436
437 \fB7.\fP に示した以外のエラーで失敗した場合や、
438 入力系の関数の一つがファイル末尾を表す 0 を返した場合は、
439 そのディスクリプタをもう一度 select に渡しては\fIならない\fP。
440 下記の例では、そのディスクリプタをただちにクローズし、
441 そこには \-1 をセットして、
442 それが集合に含まれ続けるのを許さないようにしている。
443 .TP
444 10.
445 タイムアウトの値は
446 .BR select ()
447 を呼ぶたびに初期化すべきである。
448 OS によっては timeout 構造体が変更される場合があるからである。
449 但し、
450 .BR pselect ()
451 は自分の timeout 構造体を変更することはない。
452 .TP
453 11.
454 .BR select ()
455 はファイルディスクリプタ集合を変更するので、
456 .BR select ()
457 がループの中で使用されている場合には、呼び出しを行う前に毎回
458 ディスクリプタ集合を初期化し直さなければならない。
459 .\" "I have heard" does not fill me with confidence, and doesn't
460 .\" belong in a man page, so I've commented this point out.
461 .\" .TP
462 .\" 聞いたところでは、Windows のソケット層は
463 .\" OOB データを正しく処理しないらしい。
464 .\" また、ファイルディスクリプタが全くセットされていないような
465 .\" .BR select ()
466 .\" コールも正しく処理しないらしい。
467 .\" ファイルディスクリプタを一切設定しないで timeout を使うやり方は、
468 .\" 1 秒以下の精度でプロセスをスリープさせるには便利な方法なのだが
469 .\" (続きを見よ)。
470 .SS usleep エミュレーション
471 .BR usleep (3)
472 関数を持たないシステムでは、
473 有限のタイムアウトを指定し、ファイルディスクリプタを全くセットせずに
474 .BR select ()
475 を呼び出すことで、これを代用できる。
476 以下のようにする。
477 .PP
478 .nf
479     struct timeval tv;
480     tv.tv_sec = 0;
481     tv.tv_usec = 200000;  /* 0.2 seconds */
482     select(0, NULL, NULL, NULL, &tv);
483 .fi
484 .PP
485 但し、これが動くと保証されているのは UNIX システムに限られる。
486 .SH 返り値
487 成功すると、
488 .BR select ()
489 はファイルディスクリプタ集合に残っている
490 ファイルディスクリプタの総数を返す。
491
492 .BR select ()
493 がタイムアウトすると、返り値は 0 になる。
494 その時、ファイルディスクリプタ集合はすべて空である
495 (しかしそうならないシステムもある)。
496
497 返り値が \-1 の場合はエラーを意味し、
498 .I errno
499 が適切にセットされる。エラーが起こった場合、
500 返された集合の内容や構造体 \fIstruct timeout\fP の内容は
501 未定義となっており、使用すべきではない。
502 しかし
503 .BR pselect ()
504 は決して
505 .I ntimeout
506 を変更しない。
507 .SH 注意
508 一般的に言って、ソケットをサポートする全てのオペレーティングシステムは
509 .BR select ()
510 もサポートしている。
511 .BR select ()
512 を使うと、プログラマがスレッド、フォーク、IPC、シグナル、メモリ共有、
513 等々を使ってもっと複雑な方法で解決しようとする多くの問題が、
514 移植性がありかつ効率的な方法で解決できる。
515 .PP
516 .BR poll (2)
517 システムコールは
518 .BR select ()
519 と同じ機能を持っており、
520 まばらなファイルディスクリプタ集合を監視する場合に
521 いくらか効率がよい。
522 現在では広く利用可能であるが、以前は
523 .BR select ()
524 より移植性の面で劣っていた。
525 .PP
526 Linux 独自の
527 .BR epoll (7)
528 API は、多数のファイルディスクリプタを監視する場合に
529 .BR select (2)
530
531 .BR poll (2)
532 よりも効率的なインタフェースを提供している。
533 .SH 例
534 .BR select ()
535 の本当に便利な点を示す、よい例を紹介する。
536 以下のリストは、ある TCP ポートから別のポートへ転送を行う
537 TCP フォワードプログラムである。
538 .PP
539 .nf
540 #include <stdlib.h>
541 #include <stdio.h>
542 #include <unistd.h>
543 #include <sys/time.h>
544 #include <sys/types.h>
545 #include <string.h>
546 #include <signal.h>
547 #include <sys/socket.h>
548 #include <netinet/in.h>
549 #include <arpa/inet.h>
550 #include <errno.h>
551
552 static int forward_port;
553
554 #undef max
555 #define max(x,y) ((x) > (y) ? (x) : (y))
556
557 static int
558 listen_socket(int listen_port)
559 {
560     struct sockaddr_in a;
561     int s;
562     int yes;
563
564     if ((s = socket(AF_INET, SOCK_STREAM, 0)) == \-1) {
565         perror("socket");
566         return \-1;
567     }
568     yes = 1;
569     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
570             (char *) &yes, sizeof(yes)) == \-1) {
571         perror("setsockopt");
572         close(s);
573         return \-1;
574     }
575     memset(&a, 0, sizeof(a));
576     a.sin_port = htons(listen_port);
577     a.sin_family = AF_INET;
578     if (bind(s, (struct sockaddr *) &a, sizeof(a)) == \-1) {
579         perror("bind");
580         close(s);
581         return \-1;
582     }
583     printf("accepting connections on port %d\\n", listen_port);
584     listen(s, 10);
585     return s;
586 }
587
588 static int
589 connect_socket(int connect_port, char *address)
590 {
591     struct sockaddr_in a;
592     int s;
593
594     if ((s = socket(AF_INET, SOCK_STREAM, 0)) == \-1) {
595         perror("socket");
596         close(s);
597         return \-1;
598     }
599
600     memset(&a, 0, sizeof(a));
601     a.sin_port = htons(connect_port);
602     a.sin_family = AF_INET;
603
604     if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) {
605         perror("bad IP address format");
606         close(s);
607         return \-1;
608     }
609
610     if (connect(s, (struct sockaddr *) &a, sizeof(a)) == \-1) {
611         perror("connect()");
612         shutdown(s, SHUT_RDWR);
613         close(s);
614         return \-1;
615     }
616     return s;
617 }
618
619 #define SHUT_FD1 do {                                \\
620                      if (fd1 >= 0) {                 \\
621                          shutdown(fd1, SHUT_RDWR);   \\
622                          close(fd1);                 \\
623                          fd1 = \-1;                   \\
624                      }                               \\
625                  } while (0)
626
627 #define SHUT_FD2 do {                                \\
628                      if (fd2 >= 0) {                 \\
629                          shutdown(fd2, SHUT_RDWR);   \\
630                          close(fd2);                 \\
631                          fd2 = \-1;                   \\
632                      }                               \\
633                  } while (0)
634
635 #define BUF_SIZE 1024
636
637 int
638 main(int argc, char *argv[])
639 {
640     int h;
641     int fd1 = \-1, fd2 = \-1;
642     char buf1[BUF_SIZE], buf2[BUF_SIZE];
643     int buf1_avail, buf1_written;
644     int buf2_avail, buf2_written;
645
646     if (argc != 4) {
647         fprintf(stderr, "Usage\\n\\tfwd <listen-port> "
648                  "<forward-to-port> <forward-to-ip-address>\\n");
649         exit(EXIT_FAILURE);
650     }
651
652     signal(SIGPIPE, SIG_IGN);
653
654     forward_port = atoi(argv[2]);
655
656     h = listen_socket(atoi(argv[1]));
657     if (h == \-1)
658         exit(EXIT_FAILURE);
659
660     for (;;) {
661         int r, nfds = 0;
662         fd_set rd, wr, er;
663
664         FD_ZERO(&rd);
665         FD_ZERO(&wr);
666         FD_ZERO(&er);
667         FD_SET(h, &rd);
668         nfds = max(nfds, h);
669         if (fd1 > 0 && buf1_avail < BUF_SIZE) {
670             FD_SET(fd1, &rd);
671             nfds = max(nfds, fd1);
672         }
673         if (fd2 > 0 && buf2_avail < BUF_SIZE) {
674             FD_SET(fd2, &rd);
675             nfds = max(nfds, fd2);
676         }
677         if (fd1 > 0 && buf2_avail \- buf2_written > 0) {
678             FD_SET(fd1, &wr);
679             nfds = max(nfds, fd1);
680         }
681         if (fd2 > 0 && buf1_avail \- buf1_written > 0) {
682             FD_SET(fd2, &wr);
683             nfds = max(nfds, fd2);
684         }
685         if (fd1 > 0) {
686             FD_SET(fd1, &er);
687             nfds = max(nfds, fd1);
688         }
689         if (fd2 > 0) {
690             FD_SET(fd2, &er);
691             nfds = max(nfds, fd2);
692         }
693
694         r = select(nfds + 1, &rd, &wr, &er, NULL);
695
696         if (r == \-1 && errno == EINTR)
697             continue;
698
699         if (r == \-1) {
700             perror("select()");
701             exit(EXIT_FAILURE);
702         }
703
704         if (FD_ISSET(h, &rd)) {
705             unsigned int l;
706             struct sockaddr_in client_address;
707
708             memset(&client_address, 0, l = sizeof(client_address));
709             r = accept(h, (struct sockaddr *) &client_address, &l);
710             if (r == \-1) {
711                 perror("accept()");
712             } else {
713                 SHUT_FD1;
714                 SHUT_FD2;
715                 buf1_avail = buf1_written = 0;
716                 buf2_avail = buf2_written = 0;
717                 fd1 = r;
718                 fd2 = connect_socket(forward_port, argv[3]);
719                 if (fd2 == \-1)
720                     SHUT_FD1;
721                 else
722                     printf("connect from %s\\n",
723                             inet_ntoa(client_address.sin_addr));
724             }
725         }
726
727         /* NB: read oob data before normal reads */
728
729         if (fd1 > 0)
730             if (FD_ISSET(fd1, &er)) {
731                 char c;
732
733                 r = recv(fd1, &c, 1, MSG_OOB);
734                 if (r < 1)
735                     SHUT_FD1;
736                 else
737                     send(fd2, &c, 1, MSG_OOB);
738             }
739         if (fd2 > 0)
740             if (FD_ISSET(fd2, &er)) {
741                 char c;
742
743                 r = recv(fd2, &c, 1, MSG_OOB);
744                 if (r < 1)
745                     SHUT_FD2;
746                 else
747                     send(fd1, &c, 1, MSG_OOB);
748             }
749         if (fd1 > 0)
750             if (FD_ISSET(fd1, &rd)) {
751                 r = read(fd1, buf1 + buf1_avail,
752                           BUF_SIZE \- buf1_avail);
753                 if (r < 1)
754                     SHUT_FD1;
755                 else
756                     buf1_avail += r;
757             }
758         if (fd2 > 0)
759             if (FD_ISSET(fd2, &rd)) {
760                 r = read(fd2, buf2 + buf2_avail,
761                           BUF_SIZE \- buf2_avail);
762                 if (r < 1)
763                     SHUT_FD2;
764                 else
765                     buf2_avail += r;
766             }
767         if (fd1 > 0)
768             if (FD_ISSET(fd1, &wr)) {
769                 r = write(fd1, buf2 + buf2_written,
770                            buf2_avail \- buf2_written);
771                 if (r < 1)
772                     SHUT_FD1;
773                 else
774                     buf2_written += r;
775             }
776         if (fd2 > 0)
777             if (FD_ISSET(fd2, &wr)) {
778                 r = write(fd2, buf1 + buf1_written,
779                            buf1_avail \- buf1_written);
780                 if (r < 1)
781                     SHUT_FD2;
782                 else
783                     buf1_written += r;
784             }
785
786         /* check if write data has caught read data */
787
788         if (buf1_written == buf1_avail)
789             buf1_written = buf1_avail = 0;
790         if (buf2_written == buf2_avail)
791             buf2_written = buf2_avail = 0;
792
793         /* one side has closed the connection, keep
794            writing to the other side until empty */
795
796         if (fd1 < 0 && buf1_avail \- buf1_written == 0)
797             SHUT_FD2;
798         if (fd2 < 0 && buf2_avail \- buf2_written == 0)
799             SHUT_FD1;
800     }
801     exit(EXIT_SUCCESS);
802 }
803 .fi
804 .PP
805 上記のプログラムは、ほとんどの種類の TCP 接続をフォワードする。
806 .B telnet
807 サーバによって中継される OOB シグナルデータも扱える。
808 このプログラムは、データフローを双方向に同時に送るという、
809 ややこしい問題も処理できる。
810 .BR fork (2)
811 コールを使って、各ストリームごとに専用のスレッドを用いるほうが効率的だ、
812 という人もいるかもしれない。しかし、これは考えているよりずっとややこしい。
813 あるいは、
814 .BR fcntl (2)
815 を使って非ブロック I/O をセットすれば良い、というアイデアもあるだろう。
816 これにも実際には問題があり、タイムアウトが非効率的に起こってしまう。
817
818 このプログラムは一度にひとつ以上の同時接続を扱うことはできないが、
819 その様に拡張するのは簡単で、バッファのリンクリストを
820 (接続ごとにひとつずつ) 使えばよい。
821 現時点のものでは、新しい接続がくると古い接続は落ちてしまう。
822 .SH 関連項目
823 .BR accept (2),
824 .BR connect (2),
825 .BR ioctl (2),
826 .BR poll (2),
827 .BR read (2),
828 .BR recv (2),
829 .BR select (2),
830 .BR send (2),
831 .BR sigprocmask (2),
832 .BR write (2),
833 .BR sigaddset (3),
834 .BR sigdelset (3),
835 .BR sigemptyset (3),
836 .BR sigfillset (3),
837 .BR sigismember (3),
838 .BR epoll (7)
839 .\" .SH AUTHORS
840 .\" This man page was written by Paul Sheer.