poll()を使用したechoサーバの例

#include        "unp.h"

#define OPEN_MAX 16

int
main(int argc, char **argv)
{
     int       i;
     int       max_fd_index = 0; /* 利用可能なディスクリプタの最大の添字 */
     int       listen_fd;
     int       connected_fd;
     int       socket_fd;
     int       num_of_ready_fd;
     ssize_t   n;
     char      line[MAXLINE];
     socklen_t clilen;
     struct pollfd      client[OPEN_MAX];
     struct sockaddr_in client_addr;
     struct sockaddr_in server_addr;


     /* サーバ起動の常套手順 */
     listen_fd = Socket(AF_INET, SOCK_STREAM, 0);
     bzero(&server_addr, sizeof(server_addr));
     server_addr.sin_family      = AF_INET;
     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     server_addr.sin_port        = htons(SERV_PORT);
     Bind(listen_fd, (SA *) &server_addr, sizeof(server_addr));
     Listen(listen_fd, LISTENQ);


     client[0].fd     = listen_fd;  /* 配列の最初のディスクリプタはリスニングソ
                                     * ケットである 
                                     */
                                     
     client[0].events = POLLIN;     /* リクエストイベントの設定。ここでは、読み
                                     * 出し可能となる、つまり新規コネクションが
                                     * 受け付け可能な状態になったら、pollから通
                                     * 知されるようにする。
                                     */

     /* 最初の要素以外は利用可能な状態で初期化する */
     for (i = 1; i < OPEN_MAX; i++) {
          client[i].fd = -1;
     }

     for ( ; ; ) {
          /* タイムアウト無しでディスクリプタ集合におけるイベントを待つ */
          num_of_ready_fd = Poll(client, max_fd_index+1, INFTIM);

          if (client[0].revents & POLLIN) {
               /* リスニングソケットのリターンイベントが"読み出し可能"であった場
                * 合、つまり、新規クライアントコネクションが発生した場合
                */

               clilen = sizeof(client_addr);
               connected_fd = Accept(listen_fd, (SA *) &client_addr, &clilen);

               /* エントリに余裕があれば、新規コネクションのディスクリプタをディ
                * スクリプタ集合に保存する
                */
               for (i = 1; i < OPEN_MAX; i++) {
                    if (client[i].fd < 0) {
                         client[i].fd = connected_fd;
                         break;
                    }
               }
               if (i == OPEN_MAX) {
                    err_quit("too many clients");
               }

               /* 新規コネクションを読み出し待ちでポーリングするように設定する */
               client[i].events = POLLIN;

               /* 必要なら、添字を更新する */
               if (i > max_fd_index) {
                    max_fd_index = i;
               }

               num_of_ready_fd--;
               if (num_of_ready_fd <= 0) {
                    /* 読み出し可能なディスクリプタが無いので以下の処理はスキップ */
                    continue;
               }
          }

          /* 読み出し可能なディスクリプタが存在すればここに到達する */
          for (i = 1; i <= max_fd_index; i++) {

               /* ディスクリプタが設定されている要素を検索する */
               if ( (socket_fd = client[i].fd) < 0) {
                    continue;
               }

               if (client[i].revents & (POLLIN | POLLERR)) { /* POLLERRは状況に応じて自動
                                                              * 的にセットされる 
                                                              */

                    if ( (n = readline(socket_fd, line, MAXLINE)) < 0) {
                         /* エラー処理 */

                         if (errno == ECONNRESET) {
                              /* connection reset by client */
                              Close(socket_fd);
                              client[i].fd = -1;
                         } else
                              err_sys("readline error");
                    } else if (n == 0) {
                         /* コネクションがクローズされた。Linux 2.6.17 以降ではPOLLRDHUPが
                          * 使える。
                          */

                         Close(socket_fd);
                         client[i].fd = -1;
                    } else {
                         /* 通常処理 */

                         Writen(socket_fd, line, n);
                    }

                    num_of_ready_fd--;
                    if (num_of_ready_fd <= 0) {
                         /* 読み出し可能なディスクリプタがこれ以上無い */
                         break;
                    }
               }
          }
     }
}