この付録では,ソケットおよび XTI プログラム例として,クレジット・カード登録プログラムのサーバ・プログラムとクライアント・プログラム [脚注 18] を示し,これを注釈付きで説明します。 このプログラムでは,クライアントはカード・オペレータ (merchant) に代わってサーバにアクセスし,クライアントのクレジット・カードへの代金のチャージを認めるようにサーバに許可を要求します。 サーバは登録されたカード・オペレータ (merchant) とそのパスワード,およびクレジット・カード利用者,クレジットの期限,現在の残高をデータ・ベースで管理し,この情報をもとにクライアントの要求を許可または拒否します。
以下に示されているサーバおよびクライアントのプログラム例は,コネクション指向型モードとコネクションレス型モードの各モードについて,それぞれソケット・コードと XTI コードの 2 種類のコードを使用して作成されたものです。
このプログラムは実際のアプリケーションのネットワーク・プログラミングを使用しますが,次の制限事項があります。
エラー処理を行わない
整数のみを扱う
子プロセスのクリーン・アップを行わない
B.1 節のコネクション指向型のプロトコル例では,サーバ・プログラムは要求を受信するたびにその要求の処理を行なう子プロセスをフォークし,サーバのデータ・ベース情報は子プロセス専用のデータ領域にデタッチされます。 本来,この子プロセスは要求を分析し利用者のクレジット残高を差し引く際に,同じ利用者からの次の要求を正しく処理するために,その情報 (およびいくつかのパーシステント・ストレージ) をオリジナルのサーバのデータ領域含ませるためにデータ領域の更新を行なう必要がありますが,説明上必要以上に複雑になるのをさけるためにこのロジックは含まれていません。
この付録は次のように構成されています。
コネクション指向型モード・プログラム
ソケット
サーバ
クライアント
XTI
サーバ
クライアント
コネクションレス型モード・プログラム
ソケット
サーバ
クライアント
XTI
サーバ
クライアント
共有ファイル
これらのサンプル・プログラムのコピーは
/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]
}
}
}
socket
コールでソケットを作成します。
AF_INET はインターネット通信ドメインを指定します。 OSI トランスポートがサポートされている場合は AF_INET の代わりに AF_OSI 等の対応する定数が必要となります。 ソケット・タイプ SOCK_STREAM は TCP またはコネクション指向型の通信の場合に指定されます。 このパラメータはソケットがコネクション指向型であることを示します。
XTI サーバの例 (B.1.3 項) では,socket
コールの代わりに
t_open
コールを使用します。
[例に戻る]
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
が必要になります。
[例に戻る]
INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。
すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。
詳細については,
htonl(3)htons(3)ntohl(3)ntohs(3)
SERVER_PORT は
common.h
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
0 番から1024 番まではリザーブされています。
[例に戻る]
bind
コールでソケットとサーバのアドレスをバインドします。
アドレスとポート番号の組み合わせによってネットワーク上で固有のものが識別されます。
[例に戻る]
前の
accept
コールの処理を終了するまでにサーバがキュー処理できる保留中の接続の数を指定します。
この値はサーバが
accept
コールを処理中の接続の成功レートを制御します。
複数のクライアントがサーバに
connect
要求を送信している場合に成功レートを向上させるには大きい数を使用します。
この値はオペレーティング・システムによって上限が決められます。
[例に戻る]
ソケットへの接続を受け入れます。 各接続に対して,サーバはセッションが完成するまでを処理する子プロセスをフォークします。 次にサーバは新しい接続要求のリッスンを再開します。 この例は並行型サーバのものですので,データそのものを処理する対話型サーバの例については B.2 節を参照してください。 [例に戻る]
着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログインID,パスワードおよびクレジット・カード番号等の情報をトラックする
parse
関数に渡されます。
このプロセスは
parse
関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。
クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse
関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。
このプログラムはコネクション指向型のプロトコルを使用してデータ転送を行なうので,send
および
recv
でメッセージの送受信を行ないます。
[例に戻る]
recv
コールでデータを受信します。
[例に戻る]
send
コールでデータを送信します。
[例に戻る]
例 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();
}
}
}
socket
コールでソケットを作成します。
AF_INET はインターネット通信ドメインのソケット・タイプです。 このパラメータは対応するサーバ・プログラムで選択されたプロトコルおよびタイプと一致する必要がある点に注意してください。
XTI クライアントの例 (B.1.4 項) では,socket
コールの代わりに
t_open
コールを使用します。
[例に戻る]
serveraddr
はソケットの通信ドメイン (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報は,アドレス・ファミリ (またはプロトコル) に依存する点に注意してください。
[例に戻る]
プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。
gethostbyname
ルーチンを使用してサーバの IP アドレスを知ることができます。
[例に戻る]
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
ソケット・サーバ・プログラムに接続する際には必ず同じポート番号を使用しなければなりません。
サーバおよびクライアントは通信によく使用されるアドレスとして機能するポート番号を選択します。
[例に戻る]
クライアントはサーバに接続するために,connect
コールを実行します。
connect
コールがコネクション指向型プロトコルで使用される場合,クライアントはデータを送信する前にサーバとの接続を構築することができます。
[例に戻る]
send
コールでデータを送信します。
[例に戻る]
recv
コールでデータを受信します。
[例に戻る]
例 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;
}
}
}
}
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
コールを使用します。
[例に戻る]
アドレスの選択はトランスポート・プロトコルの選択に依存します。
ソケットの例では
socket
システム・コールで使用されるものと同じアドレス・ファミリを使用しましたが,XTI ではアドレスの選択がはっきりしていないため,あらかじめトランスポート・プロトコルから
sockaddr
への適切なマッピングを確認しておく必要があります。
詳細については第 3 章を参照してください。
[例に戻る]
INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。
すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。
詳細については,
htonl(3)htons(3)ntohl(3)ntohs(3)
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
0 番から1024 番まではリザーブされています。
[例に戻る]
サーバが最後の要求を処理している際にキュー処理できる,保留中の接続の数を指定します。 [例に戻る]
t_bind
コールでサーバのアドレスをバインドします。
アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。
サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。
[例に戻る]
t_listen
関数で接続要求をリッスンをします。
[例に戻る]
他のコールから
t_open
関数への新しいトランスポートのエンドポイントを作成します。
[例に戻る]
t_accept
関数で,接続要求を受け入れます。
[例に戻る]
各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログインID,パスワードおよびクレジット・カード番号等の情報をトラックする
parse
関数に渡されます。
このプロセスは
parse
関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。
クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse
関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。
このプログラムはコネクション指向型のプロトコルを使用してデータ転送をしているので,t_snd
および
t_rcv
を使用してそれぞれメッセージの送受信を行ないます。
[例に戻る]
t_rcv
関数でデータを受信します。
[例に戻る]
t_snd
関数でデータを送信します。
[例に戻る]
例 B-4
は
B.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);
}
}
AF_INET はインターネット通信ドメインのソケット・タイプです。 AF_OSI がサポートされている場合には,AF_INET は OSI コミュニケーションのソケットの作成に使用されることもあります。 ソケット・タイプ SOCK_STREAM は TCP またはコネクション指向型コミュニケーションに指定されます。
t_open
コールは
socket
コールが必要とするソケット・アドレス・ファミリ,ソケット・タイプおよびプロトコルの代わりにデバイス特殊ファイル名を指定します。
serveraddr
はソケットの通信ドメイン (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存する点に注意してください。
[例に戻る]
AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合は OSI 通信用のソケットの作成に使用することもできます。 [例に戻る]
プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。
gethostbyname
ルーチンを使用してサーバの IP アドレスを知ることができます。
[例に戻る]
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
XTI サーバ・プログラムに接続する際には必ず同じポート番号を使用しなければなりません。
0 番から 1024 番まではリザーブされています。
[例に戻る]
t_bind
関数でサーバ・アドレスとバインドし,クライアントがデータの送受信を開始できるようにします。
[例に戻る]
t_connect
関数を使用して,サーバとの接続を制御します。
[例に戻る]
t_snd
関数でデータを送信します。
[例に戻る]
t_rcv
関数でデータを受信します。
[例に戻る]
この節では,コネクションレス型モードの通信用に書かれた,ソケット版および 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");
}
}
}
}
socket
コールでソケットを作成します。
AF_INET はインターネット通信ドメインを指定します。 ソケットタイプ SOCK_DGRAM は UDP またはコネクションレス型の通信の場合に指定されます。 このパラメータはプログラムがコネクションレス型であることを示します。
XTI サーバの例 (B.2.3 項) では,socket
コールの代わりに
t_open
コールを使用します。
[例に戻る]
serveraddr
はソケットの通信ドメイン (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報は,この例では AF_INET にあたるアドレス・ファミリに依存する点に注意してください。
AF_INET を使用する場合は
bind
コールに
sockaddr_in
が必要とされますが,AF_INET の代わりに AF_OSI を使用する場合は
sockaddr_osi
が必要になります。
[例に戻る]
INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。
すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。
詳細については,
htonl(3)htons(3)ntohl(3)ntohs(3)
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
[例に戻る]
bind
コールでソケットとサーバのアドレスをバインドします。
アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。
サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。 [例に戻る]
各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログイン ID,パスワードおよびクレジット・カード番号等の情報をトラックする
parse
関数に渡されます。
このプロセスは
parse
関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。
このプログラムはコネクションレス型の (データグラム) プロトコルを使用してデータ転送をしているので,sendto
および
recvfrom
を使用してそれぞれメッセージの送受信を行ないます。
[例に戻る]
recvfrom
コールでデータを受信します。
[例に戻る]
sendto
コールでデータを送信します。
[例に戻る]
例 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();
}
}
}
socket
コールでソケットを作成します。
AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合には,AF_INET は OSI コミュニケーションのソケットの作成に使用されることもあります。 ソケットタイプ SOCK_DGRAM は UDP またはコネクションレス型の通信の場合に指定されます。
XTI クライアント例 (B.2.4 項) では
socket
コールの代わりに
t_open
コールを使用します。
[例に戻る]
serveraddr
はソケットの通信ドメイン (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存する点に注意してください。
[例に戻る]
プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。
gethostbyname
ルーチンを使用してサーバの IP アドレスを知ることができます。
[例に戻る]
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
[例に戻る]
クライアントはサーバに接続するために
connect
コールを実行します。
connect
コールがコネクションレス型プロトコルで使用される場合,クライアントはサーバのアドレスをローカルに格納することができます。
これにより,クライアントはメッセージを送信する毎にサーバのアドレスを指定する必要がなくなります。
[例に戻る]
sendto
コールでデータを送信します。
[例に戻る]
recvfrom
コールでデータを受信します。
[例に戻る]
例 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);
}
}
t_open
コールは,この例では
/dev/streams/xtiso/udp+
にあたるデバイス特殊ファイルを指定します。
このファイル名により IP 上の UDP トランスポート・プロトコルに必要な抽象化が提供されます。
ソケット・インタフェイスと異なりアドレス・ファミリを指定する部分 (たとえば AF_INET) では,デバイス特殊ファイルの選択によって既にアドレス・ファミリの情報が取得されています。
/dev/streams/xtiso/udp+
ファイルは UDP トランスポートとインターネット・プロトコルを示します。
STREAMS デバイスの詳細については第 5 章を参照してください。
B.2.1 項で使用されている
socket
コールの代わりに,ここでは
t_open
コールを使用します。
[例に戻る]
serveraddr
はソケットの通信ドメインまたはアドレス・ファミリ (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
TCP/IP および UDP/IP では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報は,アドレス・ファミリ (またはプロトコル) に依存する点に注意してください。
[例に戻る]
AF_INET はインターネット通信ドメインまたはアドレス・ファミリを指定します。 [例に戻る]
INADDR_ANY はシステムに接続されたインタフェース・アダプタを示します。
すべての番号は適当なマクロを使用して,ネットワーク・フォーマットに変換されなければなりません。
詳細については,
htonl(3)htons(3)ntohl(3)ntohs(3)
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
0 番から 1024 番まではリザーブされています。
[例に戻る]
サーバが最後の要求を処理している間サーバがキュー処理できる保留中の接続の数を指定します。 [例に戻る]
t_bind
コールでサーバのアドレスをバインドします。
アドレスとポート番号の組み合わせでネットワーク上で固有のものを識別します。
サーバ・プロセスのアドレスがバインドされた後,サーバ・プロセスはシステムに登録されるので,低レベルのカーネル関数はこれを認識し直接要求を出すことができます。
[例に戻る]
各着信メッセージ・パケットはサーバに受け入れられた後,カード・オペレータ (merchant) のログイン ID,パスワードおよびクレジット・カード番号等の情報をトラックする
parse
関数に渡されます。
このプロセスは
parse
関数がトランザクションの終了を認識し,クライアント・プログラムに送信する応答パケットを返すまで繰り返し実行されます。
クライアント・プログラムは情報パケットを順不同で (および 1 つでも複数でも) 送信することができるので,parse
関数は構造体化されていないメッセージ・ストリームを処理するために必要なステータス情報を呼び出せる仕様になっています。
このプログラムはコネクションレス型 (データグラム) のプロトコルを使用してデータ転送をしているので,t_sndudata
および
t_rcvudata
を使用してそれぞれメッセージの送受信を行ないます。
[例に戻る]
t_rcvudata
関数でデータを受信します。
[例に戻る]
t_sndudata
関数でデータを送信します。
[例に戻る]
例 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);
}
}
t_open
コールは,たとえば
/dev/streams/xtiso/udp+
等のデバイス特殊ファイルを指定します。
このファイル名により IP 上の UDP トランスポート・プロトコルに必要な抽象化が提供されます。
ソケット・インタフェイスと異なりアドレス・ファミリを指定する部分 (たとえば AF_INET) では,デバイス特殊ファイルの選択によって既にアドレス・ファミリの情報が取得されています。
/dev/streams/xtiso/udp+
ファイルは UDP トランスポートと IP を示します。
STREAMS デバイスの詳細については第 5 章を参照してください。
B.2.2 項で使用されている
socket
コールの代わりに,ここでは
t_open
コールを使用します。
[例に戻る]
serveraddr
はソケットの通信ドメイン (AF_INET) に制御される
sockaddr_in
型の構造体です。
インターネット通信ドメインのソケット・アドレスにはネットワーク上で固有のインターネット・アドレスおよび 16 ビットのポート番号が含まれます。
UDP を含む TCP/IP プロトコル群では,サーバのインターネット・アドレスとサーバがリッスンしているポート番号になります。
sockaddr_in
構造体に含まれる情報はアドレス・ファミリ (またはプロトコル) に依存します。
[例に戻る]
AF_INET はインターネット通信ドメインを指定します。 AF_OSI がサポートされている場合は OSI 通信用のソケットの作成に使用することもできます。 [例に戻る]
プロトコルまたはアドレス・ファミリに依存するサーバについての情報を取得します。
gethostbyname(3)
SERVER_PORT は
<common.h>
ヘッダ・ファイルで定義されています。
これは short 型の整数で,サーバ・プロセスと他のアプリケーション・プロセスの識別に使用します。
[例に戻る]
t_bind
関数でサーバ・アドレスとバインドし,クライアントがデータの送受信を開始できるようにします。
[例に戻る]
t_sndudata
関数でデータを送信します。
[例に戻る]
t_rcvudata
関数でデータを受信します。
[例に戻る]
この付録に示されているアプリケーションのクライアントおよびサーバのすべてまたは一部で次のヘッダ・ファイルおよびデータベース・ファイルが必要となります。
<common.h>
<server.h>
serverauth.c
serverdb.c
xtierror.c
<client.h>
clientauth.c
clientdb.c
例 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
インクルードするヘッダ・ファイルのリスト。 [例に戻る]
定数を定義するステートメント。 この定義によって,サーバとクライアント間のデータ交換のパースがより効率的に行なわれます。 [例に戻る]
SERVER_PORT はクライアントがサーバと通信できるようにプログラマによって任意に割り当てられる最も一般的なポートです。 SERVER_PORT は接続を望むサービスの識別に使用します。 ポート番号の 0 番から 1024 番はシステム用にリザーブされています。 プログラマは他のアプリケーションと重複しないものであればどの番号でも選択することができます。 デバッグ中はこの番号はランダムに (および試行とエラーによって) 選択されます。 分散型のアプリケーションでは,他のアプリケーションとの重複を防ぐために,方針を立てる必要があります。 [例に戻る]
例 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-11
は
serverauth.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);
}
この関数はカード・オペレータ (merchant) の登録情報,利用客のクレジット・カード番号および利用者の支払い金額を含む着信データをパースします。 この関数は基礎となる TCP プロトコルがストリーム系なので,上記の情報のすべてが 1 つのメッセージで入手できると仮定することはできない点に注意してください。 この関数は,データグラム型のサービスが使用されている場合,あるいは順次パケット (SEQPACKET) を使用したプロトコルが使用されている場合単純化することができます。 この関数は情報のメッセージを順不同で単一でも複数でも受け入れるような仕様になっています。 [例に戻る]
例 B-12
は
serverdb.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-13
は
xtierror.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-14
は
client.h
ヘッダ・ファイルです。
例 B-14: client.h ファイル
#include "common.h" extern char merchantname[]; extern char password[]; extern char *swipecard();
例 B-15
は
clientauth.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-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]);
}