B    ソケットおよび XTI プログラム例

この付録では,ソケットおよび XTI プログラム例として,クレジット・カード登録プログラムのサーバ・プログラムとクライアント・プログラム [脚注 18] を示し,これを注釈付きで説明します。 このプログラムでは,クライアントはカード・オペレータ (merchant) に代わってサーバにアクセスし,クライアントのクレジット・カードへの代金のチャージを認めるようにサーバに許可を要求します。 サーバは登録されたカード・オペレータ (merchant) とそのパスワード,およびクレジット・カード利用者,クレジットの期限,現在の残高をデータ・ベースで管理し,この情報をもとにクライアントの要求を許可または拒否します。

以下に示されているサーバおよびクライアントのプログラム例は,コネクション指向型モードとコネクションレス型モードの各モードについて,それぞれソケット・コードと XTI コードの 2 種類のコードを使用して作成されたものです。

このプログラムは実際のアプリケーションのネットワーク・プログラミングを使用しますが,次の制限事項があります。

この付録は次のように構成されています。

これらのサンプル・プログラムのコピーは /usr/examples/network_programming ディレクトリから入手することができます。

B.1    コネクション指向型プログラム

この節では,コネクション指向型モードの通信用に書かれた,ソケット版および XTI 版のサーバおよびクライアントのプログラム例を示します。

B.1.1    ソケット・サーバ・プログラム

例 B-1 はソケット・インタフェースを使用するサーバをインプリメントします。

例 B-1:  コネクション指向型ソケット・サーバ・プログラム

/*
 *
 * This file contains the main socket server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       socketserver
 *
 */
 
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                   sockfd;
        int                   newsockfd;
        struct sockaddr_in    serveraddr;
        struct sockaddr_in    clientaddr;
        int                   clientaddrlen = sizeof(clientaddr);
        struct hostent        *he;
        int                   pid;
 
 
        signal(SIGCHLD, SIG_IGN);
 
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,
              sizeof(struct sockaddr_in));                     [2]
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);        [3]
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
 
        if ( bind(sockfd,                                      [5]
                  (struct sockaddr *)&serveraddr,
                  sizeof(struct sockaddr_in)) < 0) {
                perror("socket_bind");
                exit(2);
        }
 
        listen(sockfd, 8);                                     [6]
 
        while(1) {
 
                if ((newsockfd =
                      accept(sockfd,                           [7]
                             (struct sockaddr *) &clientaddr,
                             &clientaddrlen)) < 0) {
                        if (errno == EINTR) {
                                printf("Bye...\n");
                                exit(0);
                        } else {
                                perror("socket_accept");
                                exit(3);
                        }
                }
 
                pid = fork();
 
                switch(pid) {
                        case -1:        /* error */
                                perror("dosession_fork");
                                break;
                        default:
                                close(newsockfd);
                                break;
                        case 0:         /* child */
 
                                close(sockfd);
                                transactions(newsockfd);
 
                                close(newsockfd);
                                return(0);
 
                }
        }
 
}
 
 
transactions(int fd)                                           [8]
{
        int     bytes;
        char    *reply;
        int     dcount;
        char    datapipe[MAXBUFSIZE+1];
 
        /*
         * Look at the data buffer and parse commands,
         * keep track of the collected data through
         * transaction_status.
         *
         */
         while (1) {
                if ((dcount=recv(fd, datapipe, MAXBUFSIZE, 0))    [9]
                    < 0) {
                        perror("transactions_receive");
                        break;
                }
                if (dcount == 0) {
                        return(0);
                }
 
                datapipe[dcount] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
                        send(fd, reply, strlen(reply), 0);     [10]
                }
         }
}

  1. socket コールでソケットを作成します。

    AF_INET はインターネット通信ドメインを指定します。 OSI トランスポートがサポートされている場合は AF_INET の代わりに AF_OSI 等の対応する定数が必要となります。 ソケット・タイプ SOCK_STREAM は TCP またはコネクション指向型の通信の場合に指定されます。 このパラメータはソケットがコネクション指向型であることを示します。

    XTI サーバの例 (B.1.3 項) では,socket コールの代わりに t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 TCP/IP および UDP/IP では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報は,アドレス・ファミリ (この例では AF_INET) に依存する点に注意してください。 AF_INET を使用する場合は bind コールに sockaddr_in が必要とされますが,AF_INET の代わりに AF_OSI を使用する場合は sockaddr_osi が必要になります。 [例に戻る]

  3. INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。 すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。 詳細については, htonl(3)htons(3)ntohl(3) および ntohs(3) の各リファレンス・ページを参照してください。 [例に戻る]

  4. SERVER_PORT は common.h ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 0 番から1024 番まではリザーブされています。 [例に戻る]

  5. bind コールでソケットとサーバのアドレスをバインドします。 アドレスとポート番号の組み合わせによってネットワーク上で固有のものが識別されます。 [例に戻る]

  6. 前の accept コールの処理を終了するまでにサーバがキュー処理できる保留中の接続の数を指定します。 この値はサーバが accept コールを処理中の接続の成功レートを制御します。 複数のクライアントがサーバに connect 要求を送信している場合に成功レートを向上させるには大きい数を使用します。 この値はオペレーティング・システムによって上限が決められます。 [例に戻る]

  7. ソケットへの接続を受け入れます。 各接続に対して,サーバはセッションが完成するまでを処理する子プロセスをフォークします。 次にサーバは新しい接続要求のリッスンを再開します。 この例は並行型サーバのものですので,データそのものを処理する対話型サーバの例については B.2 節を参照してください。 [例に戻る]

  8. 着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログインID,パスワードおよびクレジット・カード番号等の情報をトラックする parse 関数に渡されます。 このプロセスは parse 関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。

    クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse 関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。

    このプログラムはコネクション指向型のプロトコルを使用してデータ転送を行なうので,send および recv でメッセージの送受信を行ないます。 [例に戻る]

  9. recv コールでデータを受信します。 [例に戻る]

  10. send コールでデータを送信します。 [例に戻る]

B.1.2    ソケット・クライアント・プログラム

例 B-2例 B-1 に示されている socketserver インタフェースと通信可能なクライアント・プログラムをインプリメントします。

例 B-2:  コネクション指向型ソケット・クライアント・プログラム

/*
 *
 * This generates the client program.
 *
 * usage: client [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
 
 
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     sockfd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,
              sizeof(struct sockaddr_in));                     [2]
        serveraddr.sin_family      = AF_INET;
 
        if ((serverhostp = gethostbyname(serverhost)) ==       [3]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
                          serverhostp->h_length);
 
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
        /* Now connect to the server */
        if (connect(sockfd, (struct sockaddr *)&serveraddr,    [5]
            sizeof(serveraddr)) < 0) {
                perror ("connect");
                exit(2);
        }
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (send(sockfd, buffer, strlen(buffer), 0)    [6]
                    < 0) {
                        perror("send");
                        exit(1);
                }
 
                /* receive info */
                if ((n = recv(sockfd, buffer, 1024, 0)) < 0) {    [7]
                        perror("recv");
                        exit(1);
                }
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure,"
                               " try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
 
}

  1. socket コールでソケットを作成します。

    AF_INET はインターネット通信ドメインのソケット・タイプです。 このパラメータは対応するサーバ・プログラムで選択されたプロトコルおよびタイプと一致する必要がある点に注意してください。

    XTI クライアントの例 (B.1.4 項) では,socket コールの代わりに t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報は,アドレス・ファミリ (またはプロトコル) に依存する点に注意してください。 [例に戻る]

  3. プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。 gethostbyname ルーチンを使用してサーバの IP アドレスを知ることができます。 [例に戻る]

  4. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 ソケット・サーバ・プログラムに接続する際には必ず同じポート番号を使用しなければなりません。 サーバおよびクライアントは通信によく使用されるアドレスとして機能するポート番号を選択します。 [例に戻る]

  5. クライアントはサーバに接続するために,connect コールを実行します。 connect コールがコネクション指向型プロトコルで使用される場合,クライアントはデータを送信する前にサーバとの接続を構築することができます。 [例に戻る]

  6. send コールでデータを送信します。 [例に戻る]

  7. recv コールでデータを受信します。 [例に戻る]

B.1.3    XTI サーバ・プログラム

例 B-3 はネットワーク通信に XTI ライブラリを使用するサーバをインプリメントします。 この例は,トランスポートを独立させて処理を行なう通信プログラムに相対する仕様のものです。 次のプログラムと B.1.1 項のソケット・サーバ・プログラムを比較してください。 このプログラムにはこの付録の最初に記述した制限事項が適応されます。

例 B-3:  コネクション指向型 XTI サーバ・プログラム

/*
 *
 *
 * This file contains the main XTI server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       xtiserver
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     xtifd;
        int                     newxtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     pid;
        struct t_bind           *bindreqp;
        struct t_call           *call;
 
 
        signal(SIGCHLD, SIG_IGN);
 
 
        if ((xtifd = t_open("/dev/streams/xtiso/tcp+", O_RDWR,  [1]
                            NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr, sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                  [2]
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);        [3]
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
        /* allocate structures for the t_bind and t_listen call */
        if (((bindreqp=(struct t_bind *)
                       t_alloc(xtifd, T_BIND, T_ALL))
                == NULL) ||
            ((call=(struct t_call *)
                       t_alloc(xtifd, T_CALL, T_ALL))
                == NULL)) {
                xerror("xti_alloc", xtifd);
                exit(3);
        }
 
 
        bindreqp->addr.buf      = (char *)&serveraddr;
        bindreqp->addr.len      = sizeof(serveraddr);
 
        /*
         * Specify how many pending connections can be
         * maintained, until finish accept processing
         *
         */
        bindreqp->qlen          = 8;                           [5]
 
        if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL)     [6]
            < 0) {
                xerror("xti_bind", xtifd);
                exit(4);
        }
 
        /*
         * Now the socket is ready to accept connections.
         * For each connection, fork a child process in charge
         * of the session, and then resume accepting connections.
         *
         */
 
        while(1) {
 
                if (t_listen(xtifd, &call) < 0) {              [7]
                        if (errno == EINTR) {
                                printf("Bye...\n");
                                exit(0);
                        } else {
                                xerror("t_listen", xtifd);
                                exit(4);
                        }
                }
 
                /*
                 * Create a new transport endpoint on which
                 * to accept a connection
                 *
                 */
                if ((newxtifd=t_open("/dev/streams/xtiso/tcp+", [8]
                                     O_RDWR, NULL)) < 0) {
                        xerror("xti_newopen", xtifd);
                        exit(5);
                }
 
                /* accept connection */
                if (t_accept(xtifd, newxtifd, call) < 0) {    [9]
                        xerror("xti_accept", xtifd);
                        exit(7);
                }
                pid = fork();
 
                switch(pid) {
                        case -1:        /* error */
                                xerror("dosession_fork", xtifd);
                                break;
                        default:
                                t_close(newxtifd);
                                break;
                        case 0:         /* child */
 
                                t_close(xtifd);
                                transactions(newxtifd);
                                if ((t_free((char *)bindreqp,
                                     T_BIND) < 0) ||
                                     (t_free((char *)call,
                                     T_CALL) < 0)) {
                                          xerror("xti_free", xtifd);
                                          exit(3);
                                }
 
                                t_close(newxtifd);
                                return(0);
 
                }
        }
 
}
 
 
transactions(int fd)                                           [10]
{
        int     bytes;
        char    *reply;
        int     dcount;
        int     flags;
        char    datapipe[MAXBUFSIZE+1];
 
        /*
         * Look at the data buffer and parse commands, if more data
         * required go get it
         * Since the protocol is SOCK_STREAM oriented, no data
         * boundaries will be preserved.
         *
         */
         while (1) {
                if ((dcount=t_rcv(fd, datapipe, MAXBUFSIZE,    [11]
                                  &flags)) < 0){
                        /* if disconnected bid a goodbye */
                        if (t_errno == TLOOK) {
                                int tmp = t_look(fd);
 
                                if (tmp != T_DISCONNECT) {
                                        t_scope(tmp);
                                } else {
                                        exit(0);
                                }
                        }
                        xerror("transactions_receive", fd);
                        break;
                }
                if (dcount == 0) {
                        /* consolidate all transactions */
                        return(0);
                }
 
                datapipe[dcount] = ' ';
 
                if ((reply=parse(datapipe)) != NULL) {
                        if (t_snd(fd, reply, strlen(reply), 0) [12]
                            < 0) {
                                xerror("xti_send", fd);
                                break;
                        }
                }
         }
}

  1. t_open コールは,たとえば /dev/streams/xtiso/tcp+ 等のデバイス特殊ファイルを指定します。 このファイル名により IP 上の TCP トランスポート・プロトコルに必要な抽象化が提供されます。 ソケット・インタフェイスと異なりアドレス・ファミリを指定する部分 (たとえば AF_INET) では,デバイス特殊ファイルの選択によって既にアドレス・ファミリの情報が取得されています。 /dev/streams/xtiso/tcp+ ファイルは TCP トランスポートと IP を示します。 STREAMS デバイスの詳細については第 5 章を参照してください。

    B.1.1 項で記述されているとおり,OSI トランスポートが使用できる場合は /dev/streams/xtiso/cots などのデバイスを使用することができます。

    B.1.1 項で使用されている socket コールの代わりに,ここでは t_open コールを使用します。 [例に戻る]

  2. アドレスの選択はトランスポート・プロトコルの選択に依存します。 ソケットの例では socket システム・コールで使用されるものと同じアドレス・ファミリを使用しましたが,XTI ではアドレスの選択がはっきりしていないため,あらかじめトランスポート・プロトコルから sockaddr への適切なマッピングを確認しておく必要があります。 詳細については第 3 章を参照してください。 [例に戻る]

  3. INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。 すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。 詳細については, htonl(3)htons(3)ntohl(3) および ntohs(3) の各リファレンス・ページを参照してください。 [例に戻る]

  4. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 0 番から1024 番まではリザーブされています。 [例に戻る]

  5. サーバが最後の要求を処理している際にキュー処理できる,保留中の接続の数を指定します。 [例に戻る]

  6. t_bind コールでサーバのアドレスをバインドします。 アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。 サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。 [例に戻る]

  7. t_listen 関数で接続要求をリッスンをします。 [例に戻る]

  8. 他のコールから t_open 関数への新しいトランスポートのエンドポイントを作成します。 [例に戻る]

  9. t_accept 関数で,接続要求を受け入れます。 [例に戻る]

  10. 各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログインID,パスワードおよびクレジット・カード番号等の情報をトラックする parse 関数に渡されます。 このプロセスは parse 関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。

    クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse 関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。

    このプログラムはコネクション指向型のプロトコルを使用してデータ転送をしているので,t_snd および t_rcv を使用してそれぞれメッセージの送受信を行ないます。 [例に戻る]

  11. t_rcv 関数でデータを受信します。 [例に戻る]

  12. t_snd 関数でデータを送信します。 [例に戻る]

B.1.4    XTI クライアント・プログラム

例 B-4B.1.3 項 に示されている xtiserver インタフェースと通信可能なクライアント・プログラムをインプリメントします。 例 B-3 に示されているソケット・クライアント・プログラムと比較してください。

例 B-4:  コネクション指向型 XTI クライアント・プログラム

/*
 *
 * This file contains the main XTI client code
 * for a connection-oriented mode of communication.
 *
 * Usage: xticlient [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
 
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     xtifd;
        struct sockaddr_in      serveraddr;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
        struct t_call           *sndcall;
        int                     flags = 0;
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
        if ((xtifd = t_open("/dev/streams/xtiso/tcp+", O_RDWR, [1]
                            NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                 [3]
        if ((serverhostp = gethostbyname(serverhost)) ==      [4]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
                          serverhostp->h_length);
        serveraddr.sin_port        = htons(SERVER_PORT);      [5]
 
        if (t_bind(xtifd, (struct t_bind *)NULL,              [6]
                   (struct t_bind *)NULL) < 0) {
                xerror("bind", xtifd);
                exit(2);
        }
 
        /* Allocate structures for the t_bind and t_listen calls */
        if ((sndcall=(struct t_call *)t_alloc(xtifd, T_CALL,
              T_ALL)) == NULL) {
              xerror("xti_alloc", xtifd);
              exit(3);
        }
 
        sndcall.opt.maxlen      = 0;
        sndcall.udata.maxlen    = 0;
        sndcall.addr.buf        = (char *)&serveraddr;
        sndcall.addr.len        = sizeof(serveraddr);
 
        if (t_connect(xtifd, sndcall,                         [7]
                      (struct t_call *)NULL) < 0) {
                xerror ("t_connect", xtifd);
                exit(3);
        }
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (t_snd(xtifd, buffer, strlen(buffer), 0)   [8]
                    < 0) {
                        xerror("t_snd", xtifd);
                        exit(1);
                }
 
                if ((n = t_rcv(xtifd, buffer, 1024, &flags))  [9]
                    < 0) {
                        xerror("t_rcv", xtifd);
                        exit(1);
                }
 
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure,"
                               " try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
        if (t_free((char *)sndcall, T_CALL) < 0) {
                xerror("xti_free", xtifd);
                exit(3);
        }
}

  1. AF_INET はインターネット通信ドメインのソケット・タイプです。 AF_OSI がサポートされている場合には,AF_INET は OSI コミュニケーションのソケットの作成に使用されることもあります。 ソケット・タイプ SOCK_STREAM は TCP またはコネクション指向型コミュニケーションに指定されます。

    t_open コールは socket コールが必要とするソケット・アドレス・ファミリ,ソケット・タイプおよびプロトコルの代わりにデバイス特殊ファイル名を指定します。

    B.1.2 項socket コールの代わりに t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存する点に注意してください。 [例に戻る]

  3. AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合は OSI 通信用のソケットの作成に使用することもできます。 [例に戻る]

  4. プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。 gethostbyname ルーチンを使用してサーバの IP アドレスを知ることができます。 [例に戻る]

  5. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 XTI サーバ・プログラムに接続する際には必ず同じポート番号を使用しなければなりません。 0 番から 1024 番まではリザーブされています。 [例に戻る]

  6. t_bind 関数でサーバ・アドレスとバインドし,クライアントがデータの送受信を開始できるようにします。 [例に戻る]

  7. t_connect 関数を使用して,サーバとの接続を制御します。 [例に戻る]

  8. t_snd 関数でデータを送信します。 [例に戻る]

  9. t_rcv 関数でデータを受信します。 [例に戻る]

B.2    コネクションレス型プログラム

この節では,コネクションレス型モードの通信用に書かれた,ソケット版および XTI 版のサーバおよびクライアントのプログラム例を示します。

B.2.1    ソケット・サーバ・プログラム

例 B-5 は,B.1.1 項の通信にコネクション指向型パラダイムを使用したソケット・サーバと同じ方法で,クライアント・プログラムとの通信にコネクションレス型 (データグラム/UDP) パラダイムを使用したサーバをインプリメントします。 この付録の冒頭に記述されている制限事項が適応されます。

例 B-5:  コネクションレス型ソケット・サーバ・プログラム

/*
 *
 * This file contains the main socket server code
 * for a connectionless mode of communication.
 *
 * Usage:       socketserverDG
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     sockfd;
        int                     newsockfd;
        struct sockaddr_in      serveraddr;
        int                     serveraddrlen = sizeof(serveraddr);
        struct sockaddr_in      clientaddr;
        int                     clientaddrlen = sizeof(clientaddr);
        struct hostent          *he;
        int                     pid;
 
 
        signal(SIGCHLD, SIG_IGN);
 
 
        /* Create a socket for the communications */
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))        [1]
            < 0) {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                          [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);      [3]
        serveraddr.sin_port        = htons(SERVER_PORT);     [4]
 
 
        if ( bind(sockfd,                                    [5]
                  (struct sockaddr *)&serveraddr,
                  sizeof(struct sockaddr_in)) < 0) {
                perror("socket_bind");
                exit(2);
        }
 
        transactions(sockfd);
 
}
 
 
transactions(int fd)                                         [6]
{
        int                   bytes;
        char                  *reply;
        int                   dcount;
        char                  datapipe[MAXBUFSIZE+1];
        struct sockaddr_in    serveraddr;
        int                   serveraddrlen = sizeof(serveraddr);
 
        bzero((char *) &serveraddr, sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        serveraddr.sin_port        = htons(SERVER_PORT);
 
        /* Look at the data buffer and parse commands.
         * Keep track of the collected data through
         * transaction_status.
         */
         while (1) {
                if ((dcount=recvfrom(fd, datapipe,           [7]
                                MAXBUFSIZE, 0,
                                (struct sockaddr *)&serveraddr,
                                &serveraddrlen)) < 0){
                        perror("transactions_receive");
                        break;
                }
                if (dcount == 0) {
                        return(0);
                }
 
                datapipe[dcount] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
                        if (sendto(fd, reply, strlen(reply), [8]
                            0,
                            (struct sockaddr *)&serveraddr,
                            serveraddrlen) < 0) {
                                perror("transactions_sendto");
                        }
                }
         }
}

  1. socket コールでソケットを作成します。

    AF_INET はインターネット通信ドメインを指定します。 ソケットタイプ SOCK_DGRAM は UDP またはコネクションレス型の通信の場合に指定されます。 このパラメータはプログラムがコネクションレス型であることを示します。

    XTI サーバの例 (B.2.3 項) では,socket コールの代わりに t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報は,この例では AF_INET にあたるアドレス・ファミリに依存する点に注意してください。 AF_INET を使用する場合は bind コールに sockaddr_in が必要とされますが,AF_INET の代わりに AF_OSI を使用する場合は sockaddr_osi が必要になります。 [例に戻る]

  3. INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。 すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。 詳細については, htonl(3)htons(3)ntohl(3)および ntohs(3) の各リファレンス・ページを参照してください。 [例に戻る]

  4. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 [例に戻る]

  5. bind コールでソケットとサーバのアドレスをバインドします。 アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。

    サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。 [例に戻る]

  6. 各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログイン ID,パスワードおよびクレジット・カード番号等の情報をトラックする parse 関数に渡されます。 このプロセスは parse 関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。

    このプログラムはコネクションレス型の (データグラム) プロトコルを使用してデータ転送をしているので,sendto および recvfrom を使用してそれぞれメッセージの送受信を行ないます。 [例に戻る]

  7. recvfrom コールでデータを受信します。 [例に戻る]

  8. sendto コールでデータを送信します。 [例に戻る]

B.2.2    ソケット・クライアント・プログラム

例 B-6例 B-5 に示されているソケット・サーバと通信可能なソケット・クライアント・プログラムをインプリメントします。 コネクションレス型モードまたはデータグラム・モードでソケット・インタフェースを使用します。

例 B-6:  コネクションレス型ソケット・クライアント・プログラム

/*
 *
 * This file contains the main client socket code
 * for a connectionless mode of communication.
 *
 * usage: socketclientDG [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     sockfd;
        struct sockaddr_in      serveraddr;
        int                     serveraddrlen;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
 
        /* Create a socket for the communications */
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
 
        if ((serverhostp = gethostbyname(serverhost)) ==      [3]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
              serverhostp->h_length);
 
        serveraddr.sin_port        = htons(SERVER_PORT);      [4]
 
        /* Now connect to the server
        if (connect(sockfd, (struct sockaddr *)&serveraddr,  [5]
                    sizeof(serveraddr)) < 0) {
                perror ("connect");
                exit(2);
        }
        */
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (sendto(sockfd, buffer, strlen(buffer),0,    [6]
                           (struct sockaddr *)&serveraddr,
                           sizeof(serveraddr)) < 0) {
                        perror("sendto");
                        exit(1);
                }
 
                /* receive info */
                if ((n = recvfrom(sockfd, buffer, 1024, 0,    [7]
                                  (struct sockaddr *)&serveraddr,
                                  &serveraddrlen)) < 0) {
                        perror("recvfrom");
                        exit(1);
                }
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure, "
                               "try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
 
}

  1. socket コールでソケットを作成します。

    AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合には,AF_INET は OSI コミュニケーションのソケットの作成に使用されることもあります。 ソケットタイプ SOCK_DGRAM は UDP またはコネクションレス型の通信の場合に指定されます。

    XTI クライアント例 (B.2.4 項) では socket コールの代わりに t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存する点に注意してください。 [例に戻る]

  3. プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。 gethostbyname ルーチンを使用してサーバの IP アドレスを知ることができます。 [例に戻る]

  4. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 [例に戻る]

  5. クライアントはサーバに接続するために connect コールを実行します。 connect コールがコネクションレス型プロトコルで使用される場合,クライアントはサーバのアドレスをローカルに格納することができます。 これにより,クライアントはメッセージを送信する毎にサーバのアドレスを指定する必要がなくなります。 [例に戻る]

  6. sendto コールでデータを送信します。 [例に戻る]

  7. recvfrom コールでデータを受信します。 [例に戻る]

B.2.3    XTI サーバ・プログラム

例 B-7 はネットワーク通信に XTI ライブラリを使用するサーバをインプリメントします。 この例は,トランスポートを独立させて処理を行なう通信プログラムに相対する仕様のものです。 次のプログラムと例 B-5 のソケット・サーバ・プログラムを比較してください。 このプログラムにはこの付録の最初に記述した制限事項が適応されます。

例 B-7:  コネクションレス型 XTI サーバ・プログラム

/*
 *
 * This file contains the main XTI server code
 * for a connectionless mode of communication.
 *
 * Usage:       xtiserverDG
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     xtifd;
        int                     newxtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     pid;
        struct t_bind           *bindreqp;
 
 
        signal(SIGCHLD, SIG_IGN);
 
 
        /* Create a transport endpoint for the communications */
        if ((xtifd = t_open("/dev/streams/xtiso/udp+",         [1]
                            O_RDWR, NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                 [3]
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);       [4]
        serveraddr.sin_port        = htons(SERVER_PORT)       [5]
 
        /* allocate structures for the t_bind call */
        if ((bindreqp=(struct t_bind *)t_alloc(xtifd,
                                                T_BIND,
                                                T_ALL))
                == NULL) {
                xerror("xti_alloc", xtifd);
                exit(3);
        }
 
 
        bindreqp->addr.buf      = (char *)&serveraddr;
        bindreqp->addr.len      = sizeof(serveraddr);
 
        /*
         * Specify how many pending connections can be
         * maintained, while we finish "accept" processing
         *
         */
        bindreqp->qlen          = 8;                          [6]
 
        if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL)    [7]
            < 0) {
                xerror("xti_bind", xtifd);
                exit(4);
        }
 
        /*
         * Now the server is ready to accept connections
         * on this socket.  For each connection, fork a child
         * process in charge of the session, and then resume
         * accepting connections.
         *
         */
        transactions(xtifd);
 
        if (t_free((char *)bindreqp, T_BIND) < 0) {
                xerror("xti_free", xtifd);
                exit(3);
        }
}
 
 
transactions(int fd)                                          [8]
{
        int                     bytes;
        char                    *reply;
        int                     dcount;
        int                     flags;
        char                    datapipe[MAXBUFSIZE+1];
        struct t_unitdata       *unitdata;
        struct sockaddr_in      clientaddr;
 
        /* Allocate structures for t_rcvudata and t_sndudata call */
        if ((unitdata=(struct t_unitdata *)t_alloc(fd,
                                                   T_UNITDATA,
                                                   T_ALL))
                == NULL) {
                xerror("xti_alloc", fd);
                exit(3);
        }
 
        /*
         * Look at the data buffer and parse commands.
         * If more data required, go get it.
         *
         */
         while (1) {
                unitdata->udata.maxlen   = MAXBUFSIZE;
                unitdata->udata.buf      = datapipe;
                unitdata->addr.maxlen    = sizeof(clientaddr);
                unitdata->addr.buf       = (char *)&clientaddr;
                unitdata->opt.maxlen     = 0;
 
                if ((dcount=t_rcvudata(fd, &unitdata, &flags)) [9]
                    < 0) {
                        /* if disconnected bid a goodbye */
                        if (t_errno == TLOOK) {
                                int tmp = t_look(fd);
 
                                if (tmp != T_DISCONNECT) {
                                        t_scope(tmp);
                                } else {
                                        exit(0);
                                }
                        }
                        xerror("transactions_receive", fd);
                        break;
                }
                if (unitdata->udata.len == 0) {
 
                        return(0);
                }
 
                datapipe[unitdata->udata.len] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
 
                        /* sender's addr is in the unitdata */
                        unitdata->udata.len = strlen(reply);
                        unitdata->udata.buf = reply;
 
                        if (t_sndudata(fd, unitdata) < 0) {  [10]
                                xerror("xti_send", fd);
                                break;
                        }
                }
         }
         if (t_free((char *)unitdata, T_UNITDATA) < 0) {
              xerror("xti_free", fd);
              exit(3);
         }
}

  1. t_open コールは,この例では /dev/streams/xtiso/udp+ にあたるデバイス特殊ファイルを指定します。 このファイル名により IP 上の UDP トランスポート・プロトコルに必要な抽象化が提供されます。 ソケット・インタフェイスと異なりアドレス・ファミリを指定する部分 (たとえば AF_INET) では,デバイス特殊ファイルの選択によって既にアドレス・ファミリの情報が取得されています。 /dev/streams/xtiso/udp+ ファイルは UDP トランスポートとインターネット・プロトコルを示します。 STREAMS デバイスの詳細については第 5 章を参照してください。

    B.2.1 項で使用されている socket コールの代わりに,ここでは t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメインまたはアドレス・ファミリ (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 TCP/IP および UDP/IP では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報は,アドレス・ファミリ (またはプロトコル) に依存する点に注意してください。 [例に戻る]

  3. AF_INET はインターネット通信ドメインまたはアドレス・ファミリを指定します。 [例に戻る]

  4. INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。 すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。 詳細については, htonl(3)htons(3)ntohl(3) および ntohs(3) の各リファレンス・ページを参照してください。 [例に戻る]

  5. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 0 番から 1024 番まではリザーブされています。 [例に戻る]

  6. サーバが最後の要求を処理している間サーバがキュー処理できる保留中の接続の数を指定します。 [例に戻る]

  7. t_bind コールでサーバのアドレスをバインドします。 アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。 サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。 [例に戻る]

  8. 各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログイン ID,パスワードおよびクレジット・カード番号等の情報をトラックする parse 関数に渡されます。 このプロセスは parse 関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。

    クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse 関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。

    このプログラムはコネクションレス型 (データグラム) のプロトコルを使用してデータ転送をしているので,t_sndudata および t_rcvudata を使用してそれぞれメッセージの送受信を行ないます。 [例に戻る]

  9. t_rcvudata 関数でデータを受信します。 [例に戻る]

  10. t_sndudata 関数でデータを送信します。 [例に戻る]

B.2.4    XTI クライアント・プログラム

例 B-8例 B-7 に示されている XTI サーバと通信可能なクライアント・プログラムをインプリメントします。 このプログラムはコネクションレス型モードまたはデータグラム・モードで XTI インタフェースを使用します。

例 B-8:  コネクションレス型 XTI クライアント・プログラム

/*
 *
 * This file contains the main XTI client code
 * for a connectionless mode of communication.
 *
 * usage: client [serverhostname]
 *
 */
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     xtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[MAXBUFSIZE+1];
        char                    inbuf[MAXBUFSIZE+1];
        struct t_unitdata       *unitdata;
        int                     flags = 0;
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
 
        if ((xtifd = t_open("/dev/streams/xtiso/udp+",       [1]
                            O_RDWR, NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                          [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                [3]
 
        if ((serverhostp = gethostbyname(serverhost)) ==     [4]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
              serverhostp->h_length);
        /*
         * SERVER_PORT is a short which identifies
         * the server process from other sources.
         *
         */
        serveraddr.sin_port        = htons(SERVER_PORT);     [5]
 
        if (t_bind(xtifd, (struct t_bind *)NULL,             [6]
                   (struct t_bind *)NULL) < 0) {
                xerror("bind", xtifd);
                exit(2);
        }
 
        /* Allocate structures for t_rcvudata and t_sndudata call */
        if ((unitdata=(struct t_unitdata *)t_alloc(xtifd,
                                                   T_UNITDATA,
                                                   T_ALL))
                == NULL) {
                xerror("xti_alloc", fd);
                exit(3);
        }
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
 
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
 
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                unitdata->addr.buf       = (char *)&serveraddr;
                unitdata->addr.len       = sizeof(serveraddr);
                unitdata->udata.buf      = buffer;
                unitdata->udata.len      = strlen(buffer);
                unitdata->opt.len        = 0;
 
                if (t_sndudata(xtifd, unitdata) < 0) {      [7]
                        xerror("t_snd", xtifd);
                        exit(1);
                }
 
                unitdata->udata.maxlen   = MAXBUFSIZE;
                unitdata->addr.maxlen    = sizeof(serveraddr);
 
                /* receive info */
                if ((t_rcvudata(xtifd, unitdata, &flags))   [8]
                    < 0) {
                        xerror("t_rcv", xtifd);
                        exit(1);
                }
 
                buffer[unitdata->udata.len] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure, "
                               "try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
        if (t_free((char *)unitdata, T_UNITDATA) < 0) {
             xerror("xti_free", fd);
             exit(3);
         }
}

  1. t_open コールは,たとえば /dev/streams/xtiso/udp+ 等のデバイス特殊ファイルを指定します。 このファイル名により IP 上の UDP トランスポート・プロトコルに必要な抽象化が提供されます。 ソケット・インタフェイスと異なりアドレス・ファミリを指定する部分 (たとえば AF_INET) では,デバイス特殊ファイルの選択によって既にアドレス・ファミリの情報が取得されています。 /dev/streams/xtiso/udp+ ファイルは UDP トランスポートと IP を示します。 STREAMS デバイスの詳細については第 5 章を参照してください。

    B.2.2 項で使用されている socket コールの代わりに,ここでは t_open コールを使用します。 [例に戻る]

  2. serveraddr はソケットの通信ドメイン (AF_INET) に制御される sockaddr_in 型の構造体です。 インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。 UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。

    sockaddr_in 構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存します。 [例に戻る]

  3. AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合は OSI 通信用のソケットの作成に使用することもできます。 [例に戻る]

  4. プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。 gethostbyname(3) ルーチンを使用してサーバの IP アドレスを知ることができます。 [例に戻る]

  5. SERVER_PORT は <common.h> ヘッダ・ファイルで定義されています。 これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。 [例に戻る]

  6. t_bind 関数でサーバ・アドレスとバインドし,クライアントがデータの送受信を開始できるようにします。 [例に戻る]

  7. t_sndudata 関数でデータを送信します。 [例に戻る]

  8. t_rcvudata 関数でデータを受信します。 [例に戻る]

B.3    共通コード

この付録に示されているアプリケーションのクライアントおよびサーバのすべてまたは一部で次のヘッダ・ファイルおよびデータベース・ファイルが必要となります。

B.3.1    common.h ヘッダ・ファイル

例 B-9<common.h> ヘッダ・ファイルです。 <common.h> にはこの付録のすべてのプログラム例で必要な共通ヘッダ・ファイルと定数が含まれています。

例 B-9:  common.h ヘッダ・ファイル

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>             [1]
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <xti.h>
 
 
#define SEPARATOR       ','
#define PREAMBLE        "%%"
#define PREAMBLELEN     2       [2]
#define POSTAMBLE       "##"
#define POSTAMBLELEN    2
 
 
/* How to contact the server */
#define SERVER_PORT     1234    [3]
/* How to contact the client (for datagram only) */
#define CLIENT_PORT     1235
 
#define MAXBUFSIZE 4096

  1. インクルードするヘッダ・ファイルのリスト。 [例に戻る]

  2. 定数を定義するステートメント。 この定義によって,サーバとクライアント間のデータ交換のパースがより効率的に行なわれます。 [例に戻る]

  3. SERVER_PORT はクライアントがサーバと通信できるようにプログラマによって任意に割り当てられる最も一般的なポートです。 SERVER_PORT は接続を望むサービスの識別に使用します。 ポート番号の 0 番から 1024 番はシステム用にリザーブされています。 プログラマは他のアプリケーションと重複しないものであればどの番号でも選択することができます。 デバッグ中はこの番号はランダムに (および試行とエラーによって) 選択されます。 分散型のアプリケーションでは,他のアプリケーションとの重複を防ぐために,方針を立てる必要があります。 [例に戻る]

B.3.2    server.h ヘッダ・ファイル

例 B-10<server.h> ヘッダ・ファイルです。 <server.h> にはサーバのデータベースにアクセスするためのデータ構造,クライアントからのメッセージを分析するためのデータ構造およびクライアントへのメッセージを合成するためのデータ構造が含まれます。

例 B-10:  server.h ヘッダ・ファイル

#include "common.h"
 
struct merchant {
        char *name;
        char *passwd;
};
 
struct customer {
        char                    *cardnum;
        char                    *name;
        int                     limit;
        int                     balance;
        struct transaction      *tlist;
        /* presumably other data */
};
 
struct transaction {
        struct transaction      *nextcust;
        struct transaction      *nextglob;
        struct customer         *whose;
        char                    *merchantname;
        int                     amount;
        char                    *verification;
};
 
extern struct transaction *alltransactions;
extern struct merchant  merchant[];
extern int              merchantcount;
extern struct customer  customer[];
extern int              customercount;
 
#define INVALID (struct transaction *)1
 
#define MERCHANTAUTHERROR       "%%A##"
#define USERAUTHERROR           "%%U##"
#define USERAMOUNTERROR         "%%V##"
#define TRANSMITERROR           "deadbeef"
 
/* define transaction_status flags */
#define NAME                    0x01
#define PASS                    0x02
#define AMOUNT                  0x04
#define NUMBER                  0x08
 
#define AUTHMASK                0x03
#define VERIMASK                0x0C
 

B.3.3    serverauth.c ファイル

例 B-11serverauth.c ファイルです。

例 B-11:  serverauth.c ファイル

/*
 *
 * Authorization information (not related to the
 * networking interface)
 *
 */
 
#include "server.h"
 
 
/*
 * Currently a simple non-encrypted password method to search db
 *
 */
authorizemerchant(char *merch, char *password)
{
        struct merchant *mp;
 
        for(mp = merchant; (mp)->name != (char *)NULL; mp++) {
                if (!strcmp(merch, (mp)->name)) {
                        return (!strcmp(password, (mp)->passwd));
                }
        }
        return(0);
}
 
 
struct transaction *
verifycustomer(char *num, int amount, char *merchant)
{
        char buf[64];
        struct customer         *cp;
        struct transaction      *tp;
 
        for(cp = customer; (cp)->cardnum != NULL; cp++) {
                if (!strcmp(num, (cp)->cardnum)) {
                        if (amount <= (cp)->balance) {
                                (cp)->balance -= amount;
                                if ((tp = malloc(sizeof(
                                           struct transaction)))
                                    == NULL) {
                                        printf("Malloc error\n");
                                        return(NULL);
                                }
                                tp->merchantname = merchant;
                                tp->amount = amount;
                                sprintf(buf, "v%012d", time(0));
                                if ((tp->verification =
                                     malloc(strlen(buf)+1))
                                    == NULL) {
                                        printf("Malloc err\n");
                                        return(NULL);
                                }
                                strcpy(tp->verification, buf);
                                tp->nextcust = cp->tlist;
                                tp->whose = cp;
                                cp->tlist = tp;
                                tp->nextglob = alltransactions;
                                alltransactions = tp;
                                return(tp);
                        } else {
                                return(NULL);
                        }
                }
        }
        return(INVALID);
 
}
 
int                     transaction_status;
int                     authorized = 0;
int                     amount = 0;
char                    number[256];
char                    Merchant[256];
char                    password[256];
 
 
char *
parse(char *cp)
{
        char            *dp, *ep;
        unsigned char   type;
        int             doauth = 0;
        char            *buffer;
 
        dp = cp;
        if ((buffer=malloc(256)) == NULL) {
                return(TRANSMITERROR);
        }
 
        while (*dp) {
                /* terminate the string at the postamble */
                if (!(ep=strstr(dp, POSTAMBLE))) {
                        return(TRANSMITERROR);
                }
                *ep = '\0';
                ep = ep + POSTAMBLELEN;               [1]
 
                /* search for preamble */
                if (!(dp=strstr(dp, PREAMBLE))) {
                        return(TRANSMITERROR);
                }
                dp += PREAMBLELEN;
 
                /* Now get the token */
                type = *dp++;
 
                switch(type) {
                        case 'm':
                                strcpy(Merchant, dp);
                                transaction_status |= NAME;
                                break;
                        case 'p':
                                strcpy(password, dp);
                                transaction_status |= PASS;
                                break;
                        case 'n':
                                transaction_status |= NUMBER;
                                strcpy(number, dp);
                                break;
                        case 'a':
                                transaction_status |= AMOUNT;
                                amount = atoi(dp);
                                break;
                        default:
                                printf("Bad command\n");
                                return(TRANSMITERROR);
                }
                if ((transaction_status & AUTHMASK) == AUTHMASK) {
                        transaction_status &= ~AUTHMASK;
                        authorized = authorizemerchant(
                                      Merchant, password);
                        if (!authorized) {
                                printf("Merchant not"
                                       " authorized\n");
                                return(MERCHANTAUTHERROR);
                        }
                }
 
                /* If both amount and number gathered,
                 * do verification     */
                if ((authorized) &&
                    ((transaction_status&VERIMASK)
                     ==VERIMASK)) {
                        struct transaction *tp;
 
                        transaction_status &= ~VERIMASK;
                        /* send a verification back */
                        if ((tp=verifycustomer(number,
                                               amount,
                                               Merchant))
                            == NULL) {
                                return(USERAMOUNTERROR);
                        } else if (tp==INVALID) {
                                return(USERAUTHERROR);
                        } else {
                                sprintf(buffer,
                                   "%%%%%s##%%%%c%s##%%%%m%s##",
                                   tp->verification,
                                   tp->whose->name,
                                   tp->merchantname);
                                return(buffer);
                        }
                }
 
                dp = ep;
        }
        return(NULL);
}

  1. この関数はカード・オペレータ (merchant) の登録情報,利用客のクレジット・カード番号および利用者の支払い金額を含む着信データをパースします。 この関数は基礎となる TCP プロトコルがストリーム系なので,上記の情報のすべてが 1 つのメッセージで入手できると仮定することはできない点に注意してください。 この関数は,データグラム型のサービスが使用されている場合,あるいは順次パケット (SEQPACKET) を使用したプロトコルが使用されている場合単純化することができます。 この関数は情報のメッセージを順不同で単一でも複数でも受け入れるような仕様になっています。 [例に戻る]

B.3.4    serverdb.c ファイル

例 B-12serverdb.c ファイルです。

例 B-12:  serverdb.c ファイル

/*
 *
 * Database of valid merchants and credit card customers with the
 * credit limits, etc.
 *
 *
 */
#include "server.h"
 
struct merchant merchant[] = {
        {"abc",                 "abc"},
        {"magic",               "magic"},
        {"gasco",               "gasco"},
        {"furnitureco",         "abc"},
        {"groceryco",           "groceryco"},
        {"bakeryco",            "bakeryco"},
        {"restaurantco",        "restaurantco"},
        {NULL,                  NULL}
};
 
int merchantcount = sizeof(merchant)/sizeof(struct merchant)-1;
 
struct customer customer[] = {
        { "4322546789701000", "John Smith",     1000,   800 },
        { "4322546789701001", "Bill Stone",     2000,   200 },
        { "4322546789701002", "Dave Adams",     1500,   500 },
        { "4322546789701003", "Ray Jones",      1200,   800 },
        { "4322546789701004", "Tony Zachry",    1000,   100 },
        { "4322546789701005", "Danny Einstein", 5000,   50  },
        { "4322546789701006", "Steve Simonyi",  10000,  5800},
        { "4322546789701007", "Mary Ming",      1100,   700 },
        { "4322546789701008", "Joan Walters",   800,    780 },
        { "4322546789701009", "Gail Newton",    1000,   900 },
        { "4322546789701010", "Jon Robertson",  1000,   1000},
        { "4322546789701011", "Ellen Bloop",    1300,   800 },
        { "4322546789701012", "Sue Svelter",    1400,   347 },
        { "4322546789701013", "Suzette Ring",   1200,   657 },
        { "4322546789701014", "Daniel Mattis",  1600,   239 },
        { "4322546789701015", "Robert Esconis", 1800,   768 },
        { "4322546789701016", "Lisa Stiles",    1100,   974 },
        { "4322546789701017", "Bill Brophy",    1050,   800 },
        { "4322546789701018", "Linda Smitten",  4000,   200 },
        { "4322546789701019", "John Norton",    1400,   900 },
        { "4322546789701020", "Danielle Smith", 2000,   640 },
        { "4322546789701021", "Amy Olds",       1300,   100 },
        { "4322546789701022", "Steve Smith",    2000,   832 },
        { "4322546789701023", "Robert Smart",   3000,   879 },
        { "4322546789701024", "Jon Harris",     500,    146 },
        { "4322546789701025", "Adam Gershner",  1600,   111 },
        { "4322546789701026", "Mary Papadimis", 2000,   382 },
        { "4322546789701027", "Linda Jones",    1300,   578 },
        { "4322546789701028", "Lucy Barret",    1400,   865 },
        { "4322546789701029", "Marie Gilligan", 1000,   904 },
        { "4322546789701030", "Kim Coyne",      3000,   403 },
        { "4322546789701031", "Mike Storm",     7500,   5183},
        { "4322546789701032", "Cliff Clayden",  750,    430 },
        { "4322546789701033", "John Turing",    4000,   800 },
        { "4322546789701034", "Jane Joyce",     10000,  8765},
        { "4322546789701035", "Jim Roberts",    4000,   3247},
        { "4322546789701036", "Stevw Stephano", 1750,   894 },
        {NULL, NULL}
};
 
struct transaction *
alltransactions   = NULL;
int customercount = sizeof(customer)/sizeof(struct customer)-1;
 

B.3.5    xtierror.c ファイル

例 B-13xtierror.c ファイルです。 このファイルはエラーの際の説明メッセージの生成に使用されます。 非同期エラーまたは非同期イベントについての詳細な情報の取得には,t_look 関数が使用される点に注意してください。

例 B-13:  xtierror.c ファイル

#include <xti.h>
#include <stdio.h>
 
void t_scope();
 
void
xerror(char *marker, int fd)
{
        fprintf(stderr, "%s error [%d]\n", marker, t_errno);
        t_error("Transport Error");
        if (t_errno == TLOOK) {
                t_scope(t_look(fd));
        }
 
}
 
void
t_scope(int tlook)
{
        char *tmperr;
 
        switch(tlook) {
                case T_LISTEN:
                        tmperr = "connection indication";
                        break;
                case T_CONNECT:
                        tmperr = "connect confirmation";
                        break;
                case T_DATA:
                        tmperr = "normal data received";
                        break;
                case T_EXDATA:
                        tmperr = "expedited data";
                        break;
                case T_DISCONNECT:
                        tmperr = "disconnect received";
                        break;
                case T_UDERR:
                        tmperr = "datagram error";
                        break;
                case T_ORDREL:
                        tmperr = "orderly release indication";
                        break;
                case T_GODATA:
                        tmperr = "flow control restriction lifted";
                        break;
                case T_GOEXDATA:
                        tmperr = "flow control restriction "
                                 "on expedited data lifted";
                        break;
                default:
                        tmperr = "unknown event";
        }
                        fprintf(stderr,
                                "Asynchronous event: %s\n",
                                tmperr);
}

B.3.6    client.h ヘッダ・ファイル

例 B-14client.h ヘッダ・ファイルです。

例 B-14:  client.h ファイル

#include "common.h"
 
extern char     merchantname[];
extern char     password[];
extern char     *swipecard();
 

B.3.7    clientauth.c ファイル

例 B-15clientauth.c ファイルです。 このファイルはカード・オペレータ (merchant) の登録情報を取得するコードとサーバから送信されるメッセージの分析を行なうロジックを取得するコードが含まれます。 結果として生じるメッセージは解釈され,クライアントの要求がサーバによって許可されたか拒否されたかを判断します。

例 B-15:  clientauth.c ファイル

#include "client.h"
 
init()
{
        printf("\nlogin: "); fflush(stdout);
        scanf("%s", merchantname);
 
        printf("Password: "); fflush(stdout);
        scanf("%s", password);
 
        srandom(time(0));
}
 
/* simulate some network activity via sound */
soundbytes()
{
        int i;
 
        for(i=0;i<11;i++) {
                printf();
                fflush(stdout);
                usleep(27000*(random()%10+1));
        }
}
 
analyze(char *cp)
{
 
        char *dp, *ep;
        unsigned char type;
        char customer[128];
        char verification[128];
 
        customer[0] = verification[0] = '\0';
 
        dp = cp;
 
        while ((dp!=NULL) && (*dp)) {
                /* terminate the string at the postamble */
                if (!(ep=strstr(dp, POSTAMBLE))) {
                        return(0);
                }
                *ep = '\0';
                ep = ep + POSTAMBLELEN;
 
                /* search for preamble */
                if (!(dp=strstr(dp, PREAMBLE))) {
                        return(0);
                }
                dp += PREAMBLELEN;
 
                /* Now get the token */
                type = *dp++;
 
                switch(type) {
                        case 'm':
                                if (strcmp(merchantname, dp)) {
                                        return(0);
                                }
                                break;
                        case 'c':
                                strcpy(customer, dp);
                                break;
                        case 'U':
                                printf("Authorization denied\n");
                                return(1);
                        case 'V':
                                printf("Amount exceeded\n");
                                return(1);
                        case 'A':
                                return(-1);
                        case 'v':
                                strcpy(verification, dp);
                                break;
                        default:
                                return(0);
                }
                dp = ep;
        }
        if (*customer && *verification) {
                printf("%s, verification ID: %s\n",
                       customer, verification);
                return(1);
        }
        return(0);
}
 

B.3.8    clientdb.c ファイル

例 B-16clientdb.c ファイルです。 このファイルにはカード・スワップ・アクションの擬似をするために使用される利用者のクレジット・カード番号のデータベースが含まれています。 実際のアプリケーションでは磁気リーダが適切なインタフェースを通じて番号を読み取ります。 また,実際のアプリケーションでは番号のキャッシュは必要とされません。

例 B-16:  clientdb.c ファイル

/*
 *
 * Database of customer credit card numbers to simulate
 * the card swapping action. In practice the numbers
 * will be read by magnetic readers through an
 * appropriate interface.
 */
 
#include <time.h>
 
char    merchantname[256];
char    password[256];
 
char *numbercache[] = {
        "4322546789701000",
        "4322546789701001",
        "4322546789701002",
        "4222546789701002",             /* fake id */
        "4322546789701003",
        "4322546789701004",
        "4322546789701005",
        "4322546789701006",
        "4322546789701007",
        "4322546789701008",
        "4322546789701009",
        "4322546789701010",
        "4322546789701011",
        "4322546789701012",
        "4322546789701013",
        "4322546789701014",
        "4322546789701015",
        "4322546789701016",
        "4322546789701017",
        "4322546789701018",
        "4222546789701018",             /* fake id */
        "4322546789701019",
        "4322546789701020",
        "4322546789701021",
        "4322546789701022",
        "4322546789701023",
        "4322546789701024",
        "4322546789701025",
        "2322546789701025",             /* fake id */
        "4322546789701026",
        "4322546789701027",
        "4322546789701028",
        "4322546789701029",
        "4322546789701030",
        "4322546789701031",
        "4322546789701032",
        "4322546789701033",
        "4322546789701034",
        "4322546789701035",
        "4322546789701036",
};
 
#define CACHEENTRIES (sizeof(numbercache)/sizeof(char *))
 
char *
swipecard()
{
        return(numbercache[random()%CACHEENTRIES]);
}