P.157より。select()を使用することにより、複数のディスクリプタに対してそれらの準備が出来るまで待つことが出来る。また、ソケットプログラミングにおいて相手側のコネクションがクロースするや否や、それを通知することが出来る。また、shutdown()を使用することにより、TCPをハーフクローズさせている。これにより、送信は不可でありながら受信は可能となる。

#include        "unp.h"

/*
 * echoクライアントのメイン関数。
 * 1. ファイルポインタ(普通は標準入力)から入力を受け取り、それをソケットに書き込む。
 * 2. 書き込みに応じた応答をソケットから受け取り、それを標準出力に書き出す。
 */
void
str_cli(FILE *fp, int socket_fd)
{
     int    stdin_eof_flg = 0;  /* このフラグがゼロである限りループ内で標準入力
                                 * の読み出し可能性をselect()で検査し続ける
                                 */
     int    fd;
     int    max_fd_plus1;
     fd_set rset;
     char   sendline[MAXLINE];
     char   recvline[MAXLINE];

     FD_ZERO(&1rset);              /* ディスクリプタ集合の初期化 */
     for ( ; ; ) {

          /* フラグがゼロならば標準入力を検査する。そうでなければ入力は終了して
           * いるので検査しない。
           */
          if (stdin_eof_flg == 0) {
               fd = fileno(fp);   /* fileno()は、ファイルポインタのディスクリプタを返す */
               FD_SET(fd, &rset); /* fdに対応するビットをディスクリプタ集合にセットする */
          }

          /* socket_fdに対応するビットをディスクリプタ集合にセットする */
          FD_SET(socket_fd, &rset);

          /* 2つのディスクリプタの最大値の算出。これは重要 */
          max_fd_plus1 = max(fileno(fp), socket_fd) + 1;

          /* いよいよselectの実行。どちらかのディスクリプタが読み出し可能となる
           * までブロックする。尚、ディスクリプタ集合rsetは"値-結果引数"であるの
           * で、関数から戻った時には用意できていないディスクリプタのビットはク
           * リアされている。よってselect()をcallする度に監視対象ディスクリプタ
           * をセットし直す必要が有ることに注意すること。
           *
           * ・書き込みの監視:しない
           * ・例外の監視    :しない
           * ・タイムアウト  :なし
           */
          Select(max_fd_plus1, &rset, NULL, NULL, NULL);

          if (FD_ISSET(socket_fd, &rset)) {     /* ソケットが読み出し可能となった */
               if (Readline(socket_fd, recvline, MAXLINE) == 0) {
                    if (stdin_eof_flg == 1) {

                         /* 標準入力からの入力が完了状態であり、かつ、ソケット上
                          * でEOF状態を検出した(つまりサーバがFINを発行した)ので、
                          * 正常終了とする
                          */
                         goto end;
                    }
                    else {
                         err_quit("str_cli: server terminated prematurely");
                    }
               }

               Fputs(recvline, stdout);
          }

          if (FD_ISSET(fileno(fp), &rset)) {    /* 標準入力が読み出し可能となった */
               if (Fgets(sendline, MAXLINE, fp) == NULL) {
                    stdin_eof_flg = 1;

                    /* もはやソケットへの書き込みは行わない為、shutdown()を送信
                     * 禁止でcallする。これにより対面のTCPエンドポイントへFINが
                     * 送られる。これ以後、ソケットへの送信は行われないが、受信
                     * は継続して行われる。これは、クライアントの入力が終了して
                     * もサーバへの、或いはサーバからのパイプ中にデータがある可
                     * 能性があることに対する対処である。
                     */
                    Shutdown(socket_fd, SHUT_WR);

                    /* 標準入力をディスクリプタ集合から削除する */
                    FD_CLR(fileno(fp), &rset);
                    continue;
               }

               Writen(socket_fd, sendline, strlen(sendline));
          }
     }

end:
     return;
}