プログラミングインタフェース

ソケットとサービス

ほとんどのサーバーには、既知のインターネットポート番号または UNIX ファミリ名でアクセスします。既知の UNIX ファミリ名の例には、rlogin サービスがあります。例 6–6 に、リモートログインサーバーのメインループを示します。

DEBUG モードで動作していない限り、サーバーはその呼び出し元の制御端末との関連付けを解除します。

   (void) close(0);
   (void) close(1);
   (void) close(2);
   (void) open("/", O_RDONLY);
   (void) dup2(0);
   (void) dup2(0);
   setsid();

関連付けを解除することによって、サーバーは制御端末のプロセスグループからシグナルを受信しません。制御端末との関連付けを解除したあと、サーバーはエラーレポートを制御端末に送信できません。したがって、サーバーは syslog(3C) でエラーを記録する必要があります。

サービスの定義を取得するために、サーバーは getaddrinfo(3SOCKET) を呼び出します。

    bzero(&hints, sizeof (hints));
    hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
    hints.ai_socktype = SOCK_STREAM;
    error = getaddrinfo(NULL, "rlogin", &hints, &aip);

aip に返される結果は、プログラムがサービス要求を待機するインターネットポートを定義します。標準のポート番号の一部は /usr/include/netinet/in.h で定義されています。

次に、サーバーはソケットを作成して、サービス要求を待機します。bind(3SOCKET) ルーチンを使用すると、サーバーは必ず指定された場所で待機します。リモートログインサーバーが待機するポート番号は制限されているため、サーバーはスーパーユーザーとして動作します。次のループに、サーバーのメインループ (本体) を示します。


例 6–6 サーバーのメインループ

    /* コネクション要求を待機する */
    for (;;) {
        faddrlen = sizeof (faddr);
        new_sock = accept(sock, (struct sockaddr *)&faddr, &faddrlen);
        if (new_sock == -1) {
            if (errno != EINTR && errno != ECONNABORTED) {
                perror("rlogind: accept");
            }
            continue;
        }
        if (fork() == 0) {
            close (sock);
            doit (new_sock, &faddr);
        }
        close (new_sock);
    }
    /*NOTREACHED*/

accept(3SOCKET) は、クライアントがサービスを要求するまでメッセージをブロックします。さらに、SIGCHLD などのシグナルによる割り込みを受けた場合、accept(3SOCKET) は失敗を示す値を返します。accept(3SOCKET) からの戻り値を調べて、エラーが発生している場合は syslog(3C) でエラーを記録します。

次に、サーバーは子プロセスをフォークし、リモートログインプロトコル処理の本体を呼び出します。コネクション要求を待ち行列に入れるために親プロセスが使用するソケットは、子プロセスで閉じられます。accept(3SOCKET) が作成したソケットは、親プロセスで閉じられます。クライアントのアドレスがサーバーアプリケーションの doit() ルーチンに渡され、クライアントが認証されます。