JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
プログラミングインタフェースガイド     Oracle Solaris 10 1/13 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

1.  メモリーと CPU の管理

2.  リモート共有メモリー API (Solaris クラスタ用)

3.  セッション記述プロトコル API

4.  プロセススケジューラ

5.  近傍性グループ API

6.  入出力インタフェース

7.  プロセス間通信

8.  ソケットインタフェース

SunOS 4 のバイナリ互換性

ソケットの概要

ソケットライブラリ

ソケットタイプ

インタフェースセット

ソケットの基本的な使用

ソケットの作成

ローカル名のバインド

コネクションの確立

コネクションエラー

データ転送

ソケットを閉じる

ストリームソケットのコネクション

入出力の多重化

データグラムソケット

標準ルーチン

ホスト名とサービス名

ホスト名 - hostent

ネットワーク名 - netent

プロトコル名 - protoent

サービス名 - servent

その他のルーチン

クライアントサーバープログラム

ソケットとサービス

ソケットとクライアント

コネクションレス型のサーバー

ソケットの拡張機能

帯域外データ

非ブロックソケット

非同期ソケット入出力

割り込み方式のソケット入出力

シグナルとプロセスグループ ID

特定のプロトコルの選択

アドレスのバインド

ソケットオプション

inetd デーモン

ブロードキャストとネットワーク構成の判断

マルチキャストの使用

IPv4 マルチキャストデータグラムの送信

IPv4 マルチキャストデータグラムの受信

IPv6 マルチキャストデータグラムの送信

IPv6 マルチキャストデータグラムの受信

Stream Control Transmission Protocol (SCTP)

SCTP スタックの実装

SCTP ソケットインタフェース

sctp_bindx()

sctp_opt_info()

sctp_recvmsg()

sctp_sendmsg()

sctp_send()

分岐関連付け

sctp_getpaddrs()

sctp_freepaddrs()

sctp_getladdrs()

sctp_freeladdrs()

SCTP を使用したコーディング例

9.  XTI と TLI を使用したプログラミング

10.  パケットフィルタリングフック

11.  トランスポート選択と名前からアドレスへのマッピング

12.  リアルタイムプログラミングと管理

13.  Solaris ABI と ABI ツール

A.  UNIX ドメインソケット

索引

クライアントサーバープログラム

もっとも一般的な分散型アプリケーションは、クライアントサーバーモデルです。このスキームでは、クライアントプロセスはサーバープロセスからのサービスを要求します。

代替スキームとして、休止しているサーバープロセスを削除できるサービスサーバーがあります。たとえば、inetd(1M) というインターネットサービスデーモンがあります。inetd(1M) はさまざまなポートで待機しますが、起動時に構成ファイルを読み取ることによって使用するポートを決定します。inetd(1M) のサービスを受けるポートでコネクションが要求されると、inetd(1M) はクライアントにサービスを行うために適切なサーバーを生成します。クライアントは、そのコネクションで中間媒体が何らかの役割を果たすことは意識しません。inetd(1M) の詳細は、inetd デーモン」を参照してください。

ソケットとサービス

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

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

(void) close(0);
(void) close(1);
(void) close(2);
(void) open("/", O_RDONLY);
(void) dup2(0, 1);
(void) dup2(0, 2);
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, &api);

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

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

例 8-7 サーバーのメインループ

/* Wait for a connection request. */
for (;;) {
    faddrlen = sizeof (faddr);
    new_sock = accept(sock, (struct sockaddr *)api->ai_addr,
            api->ai_addrlen)
    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() ルーチンに渡され、クライアントが認証されます。

ソケットとクライアント

このセクションでは、クライアントリモートログインプロセスで行われる処理について説明します。サーバー側と同様に、まずリモートログインのサービス定義の位置を確認します。

bzero(&hints, sizeof (hints));
hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;

error = getaddrinfo(hostname, servicename, &hints, &res);
if (error != 0) {
    (void) fprintf(stderr, "getaddrinfo: %s for host %s service %s\n",
        gai_strerror(error), hostname, servicename);
    return (-1);
}

getaddrinfo(3SOCKET) は、res にあるアドレスの一覧の先頭を返します。希望のアドレスを見つけるには、ソケットを作成し、一覧に返される各アドレスに接続して、動作するアドレスが見つかるまで繰り返します。

for (aip = res; aip != NULL; aip = aip->ai_next) {
    /*
     * Open socket.  The address type depends on what
     * getaddrinfo() gave us.
     */
    sock = socket(aip->ai_family, aip->ai_socktype,
          aip->ai_protocol);
    if (sock == -1) {
        perror("socket");
        freeaddrinfo(res);
        return (-1);
    }

    /* Connect to the host. */
    if (connect(sock, aip->ai_addr, aip->ai_addrlen) == -1) {
        perror("connect");
        (void) close(sock);
        sock = -1;
        continue;
    }
    break;
}

ソケットが作成され、希望のサービスに接続されます。sock はバインド解除されているので、connect(3SOCKET) ルーチンは暗黙的に sock をバインドします。

コネクションレス型のサーバー

一部のサービスでは、データグラムソケットを使用します。rwho(1) サービスは、LAN に接続されたホストについてのステータス情報を提供します。ネットワークトラフィックが重くなるため、in.rwhod(1M) は実行しないでください。rwho サービスは、特定のネットワークに接続されたすべてのホストに情報をブロードキャストします。rwho サービスは、データグラムソケットを使用する例の 1 つです。

rwho(1) サーバープロセスを実行するホスト上のユーザーは、ruptime(1) を使用して別のホストの現在のステータスを取得できます。次の例に、典型的な出力例を示します。

例 8-8 ruptime(1) プログラムの出力

itchy up 9:45, 5 users, load 1.15, 1.39, 1.31
scratchy up 2+12:04, 8 users, load 4.67, 5.13, 4.59
click up 10:10, 0 users, load 0.27, 0.15, 0.14
clack up 2+06:28, 9 users, load 1.04, 1.20, 1.65
ezekiel up 25+09:48, 0 users, load 1.49, 1.43, 1.41
dandy 5+00:05, 0 users, load 1.51, 1.54, 1.56
peninsula down 0:24
wood down 17:04
carpediem down 16:09
chances up 2+15:57, 3 users, load 1.52, 1.81, 1.86

各ホストには、rwho(1) サーバープロセスによってステータス情報が周期的にブロードキャスト送信されます。このサーバープロセスもステータス情報を受信します。このサーバープロセスはまた、データベースを更新します。このデータベースは、各ホストのステータスのために解釈されます。サーバーはそれぞれ個別に動作し、ローカルネットワークとそのブロードキャスト機能によってのみ結合されます。

大量のネットトラフィックが生成されるため、ブロードキャストを使用することは非効率的です。サービスが広範囲に渡り、頻繁に使用されない限り、周期的なブロードキャストに手間がかかり簡潔さが失われます。

次に、rwho(1) サーバープロセスの簡単な例を示します。このコードは、まず、ネットワーク上のほかのホストからブロードキャストされたステータス情報を受信し、次に、このコードを実行しているホストのステータス情報を提供します。最初のタスクは、プログラムのメインループで行われます。rwho(1) ポートで受信したパケットを調べて、そのパケットが別の rwho(1) サーバープロセスから送信されたことを確認し、到着時間を記録します。次に、パケットはホストのステータスでファイルを更新します。一定の時間内にホストからの通信がない場合、データベースルーチンはホストが停止していると想定し、この情報を記録します。ホストが稼働している間にはサーバーが停止していることもあるので、このアプリケーションはよくエラーになります。

例 8-9 rwho(1) サーバー

main()
{
    ...
    sp = getservbyname("who", "udp");
    net = getnetbyname("localnet");
    sin.sin6_addr = inet_makeaddr(net->n_net, in6addr_any);
    sin.sin6_port = sp->s_port;
    ...
    s = socket(AF_INET6, SOCK_DGRAM, 0);
    ...
    on = 1;
    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof on)
            == -1) {
        syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
        exit(1);
    }
    bind(s, (struct sockaddr *) &sin, sizeof sin);
    ...
    signal(SIGALRM, onalrm);
    onalrm();
    while(1) {
        struct whod wd;
        int cc, whod, len = sizeof from;
        cc = recvfrom(s, (char *) &wd, sizeof(struct whod), 0,
            (struct sockaddr *) &from, &len);
        if (cc <= 0) {
            if (cc == -1 && errno != EINTR)
                syslog(LOG_ERR, "rwhod: recv: %m");
            continue;
        }
        if (from.sin6_port != sp->s_port) {
            syslog(LOG_ERR, "rwhod: %d: bad from port",
                ntohs(from.sin6_port));
            continue;
        }
        ...
        if (!verify( wd.wd_hostname)) {
            syslog(LOG_ERR, "rwhod: bad host name from %x",
                ntohl(from.sin6_addr.s6_addr));
            continue;
        }
        (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
        whod = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
        ...
        (void) time(&wd.wd_recvtime);
        (void) write(whod, (char *) &wd, cc);
        (void) close(whod);
    }
    exit(0);
}

2 つめのサーバータスクは、そのホストのステータスの供給です。このタスクでは、周期的にシステムステータス情報を取得し、その情報をメッセージにパッケージ化し、このメッセージをローカルネットワーク上でブロードキャストして、ほかの rwho(1) サーバープロセスに知らせる必要があります。このタスクはタイマーで実行されます。このタスクはシグナルによって起動されます。

ステータス情報は、ローカルネットワーク上でブロードキャスト送信されます。ブロードキャストをサポートしないネットワークでは、マルチキャストを使用してください。