Manpages

名 前

select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO − 同 期 I/O の 多 重 化

書 式

/* POSIX.1−2001 に 従 う 場 合 */
#include <sys/select.h>

/* 以 前 の 規 格 に 従 う 場 合 */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *
exceptfds, struct timeval *utimeout);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int
fd, fd_set *set);
void FD_SET(int
fd, fd_set *set);
void FD_ZERO(fd_set *
set);

#include <sys/select.h>

int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *
exceptfds, const struct timespec *ntimeout,
const sigset_t *
sigmask);

glibc 向 け の 機 能 検 査 マ ク ロ の 要 件 (feature_test_macros(7) 参 照 ):

pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600

説 明

select() (や pselect()) を 使 う と 、 効 率 的 に 複 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー を 監 視 し 、 そ の フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が 「 ready (準 備 が で き た )」 状 態 、 つ ま り I/O (入 出 力 ) が 可 能 に な っ て い る か や 、 フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が 「 例 外 状 態 (exceptional condition)」 が 発 生 し た か 、 を 調 べ る こ と が で き る 。 こ の 関 数 の 主 要 な 引 き 数 は 、 3種 類 の フ ァ イ ル デ ィ ス ク リ プ タ ー の 「 集 合 」 readfds, writefds, exceptfds で あ る 。 各 々 の 集 合 は fd_set と し て 宣 言 さ れ 、 そ の 内 容 は FD_CLR(), FD_ISSET(), FD_SET(), FD_ZERO() と い っ た マ ク ロ に よ っ て 操 作 で き る 。 新 し く 宣 言 さ れ た 集 合 は 、 ま ず 最 初 に FD_ZERO() を 使 っ て ク リ ア す べ き で あ る 。 select() は こ れ ら の 集 合 の 内 容 を 、 以 降 に 述 べ る 規 則 に 従 っ て 修 正 す る 。 select() を 呼 ん だ 後 、 フ ァ イ ル デ ィ ス ク リ プ タ ー が ま だ 集 合 に 存 在 し て い る か ど う か は 、 FD_ISSET() マ ク ロ に よ っ て 調 べ る こ と が で き る 。 FD_ISSET() は 指 定 さ れ た デ ィ ス ク リ プ タ ー が 集 合 に 存 在 し て い れ ば 0 以 外 の 値 を 返 し 、 存 在 し な け れ ば 0 を 返 す 。 FD_CLR() は 集 合 か ら の フ ァ イ ル デ ィ ス ク リ プ タ ー の 削 除 を 行 う 。 引 き 数
readfds
こ の 集 合 に 含 ま れ る い ず れ か の フ ァ イ ル デ ィ ス ク リ プ タ ー で 、 デ ー タ の 読 み 込 み が 可 能 に な っ た か ど う か を 監 視 す る 。 select() か ら 戻 る 時 に 、 readfds の う ち 、 直 ち に 読 み 込 み 可 能 な フ ァ イ ル デ ィ ス ク リ プ タ ー 以 外 は 集 合 か ら 削 除 さ れ る 。
writefds
こ の 集 合 に 含 ま れ る い ず れ か の フ ァ イ ル デ ィ ス ク リ プ タ ー で 、 デ ー タ を 書 き 込 む ス ペ ー ス が あ る か ど う か を 監 視 す る 。 select() か ら 戻 る 時 に 、 writefds の う ち 、 直 ち に 書 き 込 み 可 能 な フ ァ イ ル デ ィ ス ク リ プ タ ー 以 外 は 集 合 か ら 削 除 さ れ る 。
exceptfds
こ の 集 合 に 含 ま れ る い ず れ か の フ ァ イ ル デ ィ ス ク リ プ タ ー で 、 「 例 外 状 態 (exceptional condition)」 が 発 生 し た か ど う か を 監 視 す る 。 実 際 の 動 作 で は 、 普 通 に 起 こ り 得 る 例 外 状 態 は 一 つ だ け で あ り 、 そ れ は TCP ソ ケ ッ ト で 帯 域 外 (out−of−band; OOB) デ ー タ が 読 み 込 み 可 能 な 場 合 で あ る 。 OOB デ ー タ の 詳 細 に つ い て は 、 recv(2), send(2), tcp(7) を 参 照 の こ と 。 (こ れ 以 外 で は 、 ま れ な こ と だ が 、 パ ケ ッ ト モ ー ド の 擬 似 端 末 (pseudoterminals) で select() が 例 外 状 態 を 示 す こ と が あ る 。 ) select() が 返 る 時 に 、 exceptfds の う ち 、 例 外 状 態 が 発 生 し た デ ィ ス ク リ プ タ ー 以 外 は 集 合 か ら 削 除 さ れ る 。

nfds 全 て の 集 合 に 含 ま れ る フ ァ イ ル デ ィ ス ク リ プ タ ー の う ち 、 値 が 最 大 の も の に

1 を 足 し た 整 数 で あ る 。 す な わ ち 、 フ ァ イ ル デ ィ ス ク リ プ タ ー を 各 集 合 に 加 え る 作 業 の 途 中 で 、 全 て の フ ァ イ ル デ ィ ス ク リ プ タ ー を 見 て 最 大 値 を 求 め 、 そ れ に 1 を 加 え て nfds と し て 渡 さ な い と い け な い 、 と い う こ と だ 。
utimeout

(何 も 起 こ ら な か っ た 場 合 に ) select() が 戻 る 前 に 待 つ 最 大 時 間 で あ る 。 こ の 値 に NULL を 渡 す と 、 select() は フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が ready (準 備 が で き た ) 状 態 に な る ま で 待 ち 続 け て ず っ と 停 止 す る 。 utimeout は 0 秒 に す る こ と も で き 、 こ の 場 合 select() は 直 ち に 返 り 、 呼 び 出 し 時 点 の フ ァ イ ル デ ィ ス ク リ プ タ ー の 状 態 に 関 す る 情 報 が 返 さ れ る 。 構 造 体 struct timeval は 次 の よ う に 定 義 さ れ て い る :

struct timeval {
time_t tv_sec; /* seconds */
long tv_usec; /* microseconds */ };

ntimeout

pselect() の こ の 引 き 数 は utimeout と 同 じ 意 味 を 持 つ が 、 struct timespec は 次 に 示 す よ う に ナ ノ 秒 の 精 度 を 持 つ 。

struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */ };

sigmask こ の 引 き 数 は 、 呼 び 出 し 側 が pselect() 内 部 で 停 止 し て い る 間 に 、 カ ー ネ ル が 通 知 を 許 可 す べ き シ グ ナ ル 集 合 (す な わ ち 、 呼 び 出 し た ス レ ッ ド の シ グ ナ ル マ ス ク か ら 削 除 す べ き シ グ ナ ル 集 合 ) を 保 持 す る (sigaddset(3) と sigprocmask(2) を 参 照 )。 こ の 引 き 数 は NULL に す る こ と も で き 、 そ の 場 合 は こ の 関 数 へ 入 る と き ・ 出 る と き に シ グ ナ ル マ ス ク を 変 更 し な い 。 こ の 場 合 、 pselect() は select() と 全 く 同 じ 動 作 と な る 。 シ グ ナ ル と デ ー タ イ ベ ン ト を 組 み 合 わ せ る フ ァ イ ル デ ィ ス ク リ プ タ ー が I/O 可 能 な 状 態 に な る の と 同 時 に シ グ ナ ル も 待 ち た い 場 合 に は 、 pselect() が 便 利 で あ る 。 シ グ ナ ル を 受 信 す る プ ロ グ ラ ム は 、 通 常 は 、 シ グ ナ ル ハ ン ド ラ ー を グ ロ ー バ ル な フ ラ グ を 立 て る た め だ け に 使 う 。 こ の グ ロ ー バ ル な フ ラ グ は 、 そ の イ ベ ン ト を プ ロ グ ラ ム の メ イ ン ル ー プ で 処 理 し な け れ ば な ら な い こ と を 示 す 。 シ グ ナ ル を 受 け る と select() (や pselect()) は errnoEINTR を セ ッ ト し て 戻 る こ と に な る 。 シ グ ナ ル が プ ロ グ ラ ム の メ イ ン ル ー プ で 処 理 さ れ る た め に は こ の 動 作 が 不 可 欠 で 、 こ れ が な い と select() は 永 遠 に 停 止 し 続 け る こ と に な る 。 さ て 、 メ イ ン ル ー プ の ど こ か に こ の グ ロ ー バ ル フ ラ グ を チ ェ ッ ク す る 条 件 文 が あ る と し よ う 。 こ こ で 少 し 考 え て み な い と い け な い 。 「 シ グ ナ ル が 条 件 文 の 後 、 し か し select() コ ー ル の 前 に 到 着 し た ら ど う な る の か ? 」 答 え は 「 そ の select() は 、 た と え 解 決 待 ち の イ ベ ン ト が あ っ た と し て も 、 永 遠 に 停 止 す る 」 で あ る 。 こ の 競 合 状 態 は pselect() コ ー ル に よ っ て 解 決 で き る 。 こ の コ ー ル を 使 う と 、 pselect() で の み 受 信 し た い シ グ ナ ル の 集 合 を シ グ ナ ル マ ス ク に 設 定 す る こ と が で き る 。 例 え ば 、 問 題 と な っ て い る イ ベ ン ト が 子 プ ロ セ ス の 終 了 の 場 合 を 考 え よ う 。 メ イ ン ル ー プ が 始 ま る 前 に 、 SIGCHLDsigprocmask(2) で ブ ロ ッ ク す る 。 pselect() コ ー ル で は SIGCHLD を 、 も と も と の シ グ ナ ル マ ス ク を 使 っ て 有 効 に す る の だ 。 こ の プ ロ グ ラ ム は 次 の よ う に な る 。

static volatile sig_atomic_t got_SIGCHLD = 0;

static void
child_sig_handler(int sig)
{
got_SIGCHLD = 1; }

int
main(int argc, char *argv[])
{
sigset_t sigmask, empty_mask;
struct sigaction sa;
fd_set readfds, writefds, exceptfds;
int r;

sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == −1) {
perror("sigprocmask");
exit(EXIT_FAILURE); }

sa.sa_flags = 0;
sa.sa_handler = child_sig_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == −1) {
perror("sigaction");
exit(EXIT_FAILURE); }

sigemptyset(&empty_mask);

for (;;) { /* main loop */
/* Initialize readfds, writefds, and exceptfds
before the pselect() call. (Code omitted.) */

r = pselect(nfds, &readfds, &writefds, &exceptfds,
NULL, &empty_mask);
if (r == −1 && errno != EINTR) {
/* Handle error */ }

if (got_SIGCHLD) {
got_SIGCHLD = 0;

/* Handle signalled event here; e.g., wait() for all
terminated children. (Code omitted.) */ }

/* main body of program */ } } 実 例 実 際 の と こ ろ select() の 大 事 な 点 は 何 な の か ? デ ィ ス ク リ プ タ ー は 好 き な と き に 読 み 書 き で き る ん じ ゃ な い の ? select() の 重 要 な と こ ろ は 、 複 数 の デ ィ ス ク リ プ タ ー を 同 時 に 監 視 で き 、 な ん の 動 き も な け れ ば プ ロ セ ス を 適 切 に ス リ ー プ 状 態 に 移 行 す る と こ ろ に あ る の だ 。 UNIX プ ロ グ ラ マ は 、 複 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー の 入 出 力 を 同 時 に 扱 わ ね ば な ら ず 、 し か も デ ー タ の 流 れ は 間 欠 的 で あ る 、 と い う 状 況 に よ く 出 会 う 。 単 に read(2)write(2) コ ー ル の シ ー ケ ン ス を 作 る だ け で は 、 そ れ ら の コ ー ル の ど れ か が フ ァ イ ル デ ィ ス ク リ プ タ ー か ら の デ ー タ を 待 っ て ブ ロ ッ ク し て お り 、 別 の フ ァ イ ル デ ィ ス ク リ プ タ ー に は I/O が 可 能 な の に 使 え な い 、 と い う こ と に な っ て し ま う だ ろ う 。 select() を 使 う と こ の 状 況 に 効 果 的 に 対 処 で き る 。

SELECT の 掟
select
() を 使 お う と し た 多 く の 人 は 、 理 解 し に く い 挙 動 に 出 く わ し 、 結 果 的 に で き た も の は 移 植 性 が な い か 、 よ く て も ギ リ ギ リ の も の に な っ て し ま う 。 例 え ば 、 上 記 の プ ロ グ ラ ム は 、 集 合 に 含 ま れ る フ ァ イ ル デ ィ ス ク リ プ タ ー を 非 停 止 (nonblocking) モ ー ド に し な く て も 、 ど こ に も ブ ロ ッ ク が 生 じ な い よ う 注 意 し て 書 か れ て い る 。 微 妙 な 間 違 い に よ っ て 、 select() を 使 う 利 点 は 簡 単 に 失 わ れ て し ま う 。 そ こ で 、 select() コ ー ル を 使 う と き に 注 意 す べ き 重 要 事 項 を 列 挙 し て お く こ と に す る 。

1.

select() を 使 う と き は 、 タ イ ム ア ウ ト は 設 定 す べ き で な い 。 処 理 す る デ ー タ が 無 い と き に は 、 あ な た の プ ロ グ ラ ム に は 何 も す る こ と は 無 い は ず で あ る 。 タ イ ム ア ウ ト に 依 存 し た コ ー ド は 通 常 移 植 性 が な く 、 デ バ ッ グ も 難 し く な る 。

2.

上 述 し た よ う に 、 効 率 的 な プ ロ グ ラ ム を 書 く に は nfds の 値 を 適 切 に 計 算 し て 与 え な け れ ば な ら な い 。
3.

select() コ ー ル の 終 了 後 に 結 果 を チ ェ ッ ク し て 、 適 切 に 対 応 す る つ も り の な い フ ァ イ ル デ ィ ス ク リ プ タ ー は 、 ど の 集 合 に も 加 え て は な ら な い 。 次 の ル ー ル も 参 照 。

4.

select() か ら 返 っ た 後 に は 、 全 て の 集 合 の 全 て の フ ァ イ ル デ ィ ス ク リ プ タ ー に つ い て 読 み 書 き 可 能 な 状 態 に な っ て い る か を チ ェ ッ ク す べ き で あ る 。

5.

read(2), recv(2), write(2), send(2) と い っ た 関 数 は 、 こ ち ら が 要 求 し た 全 デ ー タ を 読 み 書 き す る 必 要 は な い 。 も し 全 デ ー タ を 読 み 書 き す る な ら 、 そ れ は ト ラ フ ィ ッ ク の 負 荷 が 小 さ く 、 ス ト リ ー ム が 速 い 場 合 だ ろ う 。 こ の 条 件 は 常 に 満 た さ れ る と は 限 ら な い 。 こ れ ら の 関 数 が 頑 張 っ て も 1 バ イ ト し か 送 受 信 で き な い よ う な 場 合 も 考 慮 に 入 れ て や ら な け れ ば な ら な い 。

6.

処 理 す る デ ー タ 量 が 小 さ い こ と が は っ き り と わ か っ て い る 場 合 を 除 い て 、 一 度 に 1 バ イ ト ず つ 読 み 書 き す る よ う な こ と は し て は な ら な い 。 バ ッ フ ァ ー の 許 す か ぎ り の デ ー タ を ま と め て 読 み 書 き し な い と 、 非 常 に 効 率 が 悪 い 。 下 記 の 例 で は バ ッ フ ァ ー は 1024 バ イ ト に し て い る が 、 こ の サ イ ズ を 大 き く す る の は 簡 単 だ ろ う 。
7.

read(2), recv(2), write(2), send(2) な ど の 関 数 や select() コ ー ル は 、 errnoEINTR や EAGAIN (EWOULDBLOCK) に し て −1 を 返 す こ と が あ る 。 こ の よ う な 結 果 に 対 し て 適 切 に 対 応 し て や ら な け れ ば な ら な い (上 記 の 例 で は し て い な い )。 書 い て い る プ ロ グ ラ ム が シ グ ナ ル を 受 け る 予 定 が な け れ ば 、 EINTR が 返 さ れ る こ と は あ ま り 考 え ら れ な い 。 書 い て い る プ ロ グ ラ ム で 非 ブ ロ ッ ク I/O を セ ッ ト し て い な い 場 合 は 、 EAGAIN が 返 さ れ る こ と は な い だ ろ う 。

8.

決 し て 、 引 き 数 に 長 さ 0 の バ ッ フ ァ ー を 指 定 し て read(2), recv(2), write(2), send(2) を 呼 び 出 し て は な ら な い 。
9.

read(2), recv(2), write(2), send(2)7. に 示 し た 以 外 の エ ラ ー で 失 敗 し た 場 合 や 、 入 力 系 の 関 数 の 一 つ が フ ァ イ ル 末 尾 を 表 す 0 を 返 し た 場 合 は 、 そ の デ ィ ス ク リ プ タ ー を も う 一 度 select に 渡 し て は な ら な い 。 下 記 の 例 で は 、 そ の デ ィ ス ク リ プ タ ー を た だ ち に ク ロ ー ズ し 、 そ こ に は −1 を セ ッ ト し て 、 そ れ が 集 合 に 含 ま れ 続 け る の を 許 さ な い よ う に し て い る 。

10.

タ イ ム ア ウ ト の 値 は select() を 呼 ぶ た び に 初 期 化 す べ き で あ る 。 OS に よ っ て は timeout 構 造 体 が 変 更 さ れ る 場 合 が あ る か ら で あ る 。 但 し 、 pselect() は 自 分 の timeout 構 造 体 を 変 更 す る こ と は な い 。
11.

select() は フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 変 更 す る の で 、 select() が ル ー プ の 中 で 使 用 さ れ て い る 場 合 に は 、 呼 び 出 し を 行 う 前 に 毎 回 デ ィ ス ク リ プ タ ー 集 合 を 初 期 化 し 直 さ な け れ ば な ら な い 。

usleep エ ミ ュ レ ー シ ョ ン
usleep(3) 関 数 を 持 た な い シ ス テ ム で は 、 有 限 の タ イ ム ア ウ ト を 指 定 し 、 フ ァ イ ル デ ィ ス ク リ プ タ ー を 全 く セ ッ ト せ ず に select() を 呼 び 出 す こ と で 、 こ れ を 代 用 で き る 。 以 下 の よ う に す る 。

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000; /* 0.2 seconds */
select(0, NULL, NULL, NULL, &tv); 但 し 、 こ れ が 動 く と 保 証 さ れ て い る の は UNIX シ ス テ ム に 限 ら れ る 。

返 り 値

成 功 す る と 、 select() は フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 に 残 っ て い る フ ァ イ ル デ ィ ス ク リ プ タ ー の 総 数 を 返 す 。

select() が タ イ ム ア ウ ト す る と 、 返 り 値 は 0 に な る 。 そ の 時 、 フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 は す べ て 空 で あ る (し か し そ う な ら な い シ ス テ ム も あ る )。 返 り 値 が −1 の 場 合 は エ ラ ー を 意 味 し 、 errno が 適 切 に セ ッ ト さ れ る 。 エ ラ ー が 起 こ っ た 場 合 、 返 さ れ た 集 合 の 内 容 や 構 造 体 struct timeout の 内 容 は 未 定 義 と な っ て お り 、 使 用 す べ き で は な い 。 し か し pselect() は 決 し て ntimeout を 変 更 し な い 。

注 意

一 般 的 に 言 っ て 、 ソ ケ ッ ト を サ ポ ー ト す る 全 て の オ ペ レ ー テ ィ ン グ シ ス テ ム は select() も サ ポ ー ト し て い る 。 select() を 使 う と 、 プ ロ グ ラ マ が ス レ ッ ド 、 フ ォ ー ク 、 IPC、 シ グ ナ ル 、 メ モ リ ー 共 有 、 等 々 を 使 っ て も っ と 複 雑 な 方 法 で 解 決 し よ う と す る 多 く の 問 題 が 、 移 植 性 が あ り か つ 効 率 的 な 方 法 で 解 決 で き る 。

poll(2) シ ス テ ム コ ー ル は select() と 同 じ 機 能 を 持 っ て お り 、 ま ば ら な フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 監 視 す る 場 合 に い く ら か 効 率 が よ い 。 現 在 で は 広 く 利 用 可 能 で あ る が 、 以 前 は select() よ り 移 植 性 の 面 で 劣 っ て い た 。

Linux 独 自 の epoll(7) API は 、 多 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー を 監 視 す る 場 合 に select(2)poll(2) よ り も 効 率 的 な イ ン タ ー フ ェ ー ス を 提 供 し て い る 。

select() の 本 当 に 便 利 な 点 を 示 す 、 よ い 例 を 紹 介 す る 。 以 下 の リ ス ト は 、 あ る TCP ポ ー ト か ら 別 の ポ ー ト へ 転 送 を 行 う TCP フ ォ ワ ー ド プ ロ グ ラ ム で あ る 。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

static int forward_port;

#undef max
#define max(x,y) ((x) > (y) ? (x) : (y))

static int
listen_socket(int listen_port)
{
struct sockaddr_in a;
int s;
int yes;

if ((s = socket(AF_INET, SOCK_STREAM, 0)) == −1) {
perror("socket");
return −1; }
yes = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(yes)) == −1) {
perror("setsockopt");
close(s);
return −1; }
memset(&a, 0, sizeof(a));
a.sin_port = htons(listen_port);
a.sin_family = AF_INET;
if (bind(s, (struct sockaddr *) &a, sizeof(a)) == −1) {
perror("bind");
close(s);
return −1; }
printf("accepting connections on port %d\n", listen_port);
listen(s, 10);
return s; }

static int
connect_socket(int connect_port, char *address)
{
struct sockaddr_in a;
int s;

s = socket(AF_INET, SOCK_STREAM, 0);
if (s == −1) {
perror("socket");
close(s);
return −1; }

memset(&a, 0, sizeof(a));
a.sin_port = htons(connect_port);
a.sin_family = AF_INET;

if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) {
perror("bad IP address format");
close(s);
return −1; }

if (connect(s, (struct sockaddr *) &a, sizeof(a)) == −1) {
perror("connect()");
shutdown(s, SHUT_RDWR);
close(s);
return −1; }
return s; }

#define SHUT_FD1 do { \
if (fd1 >= 0) { \
shutdown(fd1, SHUT_RDWR); \
close(fd1); \
fd1 = −1; \ }
\ }
while (0)

#define SHUT_FD2 do { \
if (fd2 >= 0) { \
shutdown(fd2, SHUT_RDWR); \
close(fd2); \
fd2 = −1; \ }
\ }
while (0)

#define BUF_SIZE 1024

int
main(int argc, char *argv[])
{
int h;
int fd1 = −1, fd2 = −1;
char buf1[BUF_SIZE], buf2[BUF_SIZE];
int buf1_avail, buf1_written;
int buf2_avail, buf2_written;

if (argc != 4) {
fprintf(stderr, "Usage\n\tfwd <listen−port> "
"<forward−to−port> <forward−to−ip−address>\n");
exit(EXIT_FAILURE); }

signal(SIGPIPE, SIG_IGN);

forward_port = atoi(argv[2]);

h = listen_socket(atoi(argv[1]));
if (h == −1)
exit(EXIT_FAILURE);

for (;;) {
int r, nfds = 0;
fd_set rd, wr, er;

FD_ZERO(&rd);
FD_ZERO(&wr);
FD_ZERO(&er);
FD_SET(h, &rd);
nfds = max(nfds, h);
if (fd1 > 0 && buf1_avail < BUF_SIZE) {
FD_SET(fd1, &rd);
nfds = max(nfds, fd1); }
if (fd2 > 0 && buf2_avail < BUF_SIZE) {
FD_SET(fd2, &rd);
nfds = max(nfds, fd2); }
if (fd1 > 0 && buf2_avail − buf2_written > 0) {
FD_SET(fd1, &wr);
nfds = max(nfds, fd1); }
if (fd2 > 0 && buf1_avail − buf1_written > 0) {
FD_SET(fd2, &wr);
nfds = max(nfds, fd2); }
if (fd1 > 0) {
FD_SET(fd1, &er);
nfds = max(nfds, fd1); }
if (fd2 > 0) {
FD_SET(fd2, &er);
nfds = max(nfds, fd2); }

r = select(nfds + 1, &rd, &wr, &er, NULL);

if (r == −1 && errno == EINTR)
continue;

if (r == −1) {
perror("select()");
exit(EXIT_FAILURE); }

if (FD_ISSET(h, &rd)) {
unsigned int l;
struct sockaddr_in client_address;

memset(&client_address, 0, l = sizeof(client_address));
r = accept(h, (struct sockaddr *) &client_address, &l);
if (r == −1) {
perror("accept()"); }
else {
SHUT_FD1;
SHUT_FD2;
buf1_avail = buf1_written = 0;
buf2_avail = buf2_written = 0;
fd1 = r;
fd2 = connect_socket(forward_port, argv[3]);
if (fd2 == −1)
SHUT_FD1;
else
printf("connect from %s\n",
inet_ntoa(client_address.sin_addr)); } }

/* NB: read oob data before normal reads */

if (fd1 > 0)
if (FD_ISSET(fd1, &er)) {
char c;

r = recv(fd1, &c, 1, MSG_OOB);
if (r < 1)
SHUT_FD1;
else
send(fd2, &c, 1, MSG_OOB); }
if (fd2 > 0)
if (FD_ISSET(fd2, &er)) {
char c;

r = recv(fd2, &c, 1, MSG_OOB);
if (r < 1)
SHUT_FD2;
else
send(fd1, &c, 1, MSG_OOB); }
if (fd1 > 0)
if (FD_ISSET(fd1, &rd)) {
r = read(fd1, buf1 + buf1_avail,
BUF_SIZE − buf1_avail);
if (r < 1)
SHUT_FD1;
else
buf1_avail += r; }
if (fd2 > 0)
if (FD_ISSET(fd2, &rd)) {
r = read(fd2, buf2 + buf2_avail,
BUF_SIZE − buf2_avail);
if (r < 1)
SHUT_FD2;
else
buf2_avail += r; }
if (fd1 > 0)
if (FD_ISSET(fd1, &wr)) {
r = write(fd1, buf2 + buf2_written,
buf2_avail − buf2_written);
if (r < 1)
SHUT_FD1;
else
buf2_written += r; }
if (fd2 > 0)
if (FD_ISSET(fd2, &wr)) {
r = write(fd2, buf1 + buf1_written,
buf1_avail − buf1_written);
if (r < 1)
SHUT_FD2;
else
buf1_written += r; }

/* check if write data has caught read data */

if (buf1_written == buf1_avail)
buf1_written = buf1_avail = 0;
if (buf2_written == buf2_avail)
buf2_written = buf2_avail = 0;

/* one side has closed the connection, keep
writing to the other side until empty */

if (fd1 < 0 && buf1_avail − buf1_written == 0)
SHUT_FD2;
if (fd2 < 0 && buf2_avail − buf2_written == 0)
SHUT_FD1; }
exit(EXIT_SUCCESS); } 上 記 の プ ロ グ ラ ム は 、 ほ と ん ど の 種 類 の TCP 接 続 を フ ォ ワ ー ド す る 。 telnet サ ー バ に よ っ て 中 継 さ れ る OOB シ グ ナ ル デ ー タ も 扱 え る 。 こ の プ ロ グ ラ ム は 、 デ ー タ フ ロ ー を 双 方 向 に 同 時 に 送 る と い う 、 や や こ し い 問 題 も 処 理 で き る 。 fork(2) コ ー ル を 使 っ て 、 各 ス ト リ ー ム ご と に 専 用 の ス レ ッ ド を 用 い る ほ う が 効 率 的 だ 、 と い う 人 も い る か も し れ な い 。 し か し 、 こ れ は 考 え て い る よ り ず っ と や や こ し い 。 あ る い は 、 fcntl(2) を 使 っ て 非 ブ ロ ッ ク I/O を セ ッ ト す れ ば 良 い 、 と い う ア イ デ ア も あ る だ ろ う 。 こ れ に も 実 際 に は 問 題 が あ り 、 タ イ ム ア ウ ト が 非 効 率 的 に 起 こ っ て し ま う 。 こ の プ ロ グ ラ ム は 一 度 に ひ と つ 以 上 の 同 時 接 続 を 扱 う こ と は で き な い が 、 そ の 様 に 拡 張 す る の は 簡 単 で 、 バ ッ フ ァ ー の リ ン ク リ ス ト を (接 続 ご と に ひ と つ ず つ ) 使 え ば よ い 。 現 時 点 の も の で は 、 新 し い 接 続 が く る と 古 い 接 続 は 落 ち て し ま う 。

関 連 項 目

accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2), select(2), send(2), sigprocmask(2), write(2), sigaddset(3), sigdelset(3), sigemptyset(3), sigfillset(3), sigismember(3), epoll(7)

こ の 文 書 に つ い て

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