bea ホーム | 製品 | dev2dev | support | askBEA |
![]() |
![]() |
|
![]() |
e-docs > Tuxedo > C 言語を使用した Tuxedo アプリケーションのプログラミング > クライアントおよびサーバへの要求/応答のコーディング |
C 言語を使用した Tuxedo アプリケーションのプログラミング
|
クライアントおよびサーバへの要求/応答のコーディング
ここでは、次の内容について説明します。
要求/応答通信の概要
要求/応答通信モードでは、あるソフトウェア・モジュールが別のソフトウェア・モジュールに要求を送り、応答を待ちます。最初のソフトウェア・モジュールがクライアント、2 番目のソフトウェア・モジュールがサーバとして動作するので、このモードはクライアント/サーバ相互作用とも呼ばれます。オンラインの銀行業務の多くは、要求/応答モードでプログラミングされます。たとえば、残高照会の要求は、次のように行われます。
図 6-1 オンライン銀行業務での応答/要求通信
クライアント・プロセスがアプリケーションに参加し、バッファを割り当て、入力データ要求をバッファに格納すると、そのプロセスは要求メッセージをサービス・サブルーチンに送信したり、応答メッセージを受信することができるようになります。
同期メッセージの送信
tpcall(3c) 関数は、サービス・サブルーチンに要求を送信し、同期的に応答を待ちます。tpcall() 関数の呼び出しには、次の文法を使用します。
int
tpcall(char *svc, char *idata, long ilen, char **odata, long *olen, long flags)
次の表は、tpcall() 関数の引数を示しています。
表 6-1 tpcall( ) 関数の引数
tpcall() は、応答を待ちます。 注記 tpcall() 関数の呼び出しは、tpacall() を呼び出した直後に tpgetrply() を呼び出すことと論理的には同じです。非同期メッセージの送信を参照してください。 要求は、svc で指定されたサービスの優先順位で送信されます。ただし、tpsprio() の呼び出しで明示的に異なる優先順位が設定されている場合は除きます。詳細については、メッセージの優先順位の設定および取得を参照してください。 tpcall() は整数を返します。失敗した場合、この整数値は -1 になり、tperrno(5) に発生したエラー条件が返されます。有効なエラー・コードの詳細については、『BEA Tuxedo C リファレンス』の tpcall(3c) を参照してください。 注記 通信呼び出しはいろいろな原因で失敗します。そのほとんどは、アプリケーション・レベルで修正することができます。失敗の原因としては、アプリケーション定義のエラー (TPESVCFAIL)、戻り値の処理エラー (TPESVCERR)、型付きバッファのエラー (TPEITYPE、TPEOTYPE)、タイムアウト・エラー (TPETIME)、プロトコル・エラー (TPEPROTO) などがあります。エラーの詳細については、エラーの管理を参照してください。発生する可能性があるエラーについては、『BEA Tuxedo C リファレンス』の tpcall(3c) を参照してください。 BEA Tuxedo システムでは、割り当てられているバッファより大きなメッセージを受信した場合、メッセージ受信用バッファのサイズが自動的に変更されます。そのため、応答バッファのサイズが変更されたかどうかを確認する必要があります。 バッファの新しいサイズにアクセスするには、*olen パラメータに返されたアドレスを使用します。応答バッファのサイズが変更されたかどうかを確認するには、tpcall() を呼び出す前の応答バッファのサイズと、返された応答バッファの *olen の値とを比較します。*olen が元の値より大きい場合、バッファのサイズは大きくなっています。それ以外の場合、バッファのサイズは変更されていません。 呼び出しの後に odata に戻された値で、出力バッファを参照します。サイズの増加以外に、出力バッファが変更されている場合があるからです。要求バッファのサイズを確認する必要はありません。要求データは一度割り当てられると、調整されないからです。 注記 要求メッセージと応答メッセージに同じバッファを使用している場合に、バッファのサイズが変更されたために応答バッファを指すポインタが変更されたときは、入力バッファへのポインタは有効なアドレスを参照しなくなります。 例: 要求メッセージと応答メッセージに同じバッファを使用する 以下のコード例は、クライアント・プログラム audit.c で、要求メッセージと応答メッセージに同じバッファを使用して、同期呼び出しを行う方法を示しています。この例では、*audv メッセージ・バッファは要求情報と応答情報の両方を格納するように設定されているので、同じバッファを使用することができます。このコードでは、次の処理が行われます。
コード リスト 6-1 要求メッセージと応答メッセージに同じバッファを使用する
. . .
/* バッファを作成し、データ・ポインタを設定します。 */
audv = (struct aud *)tpalloc("VIEW", "aud", sizeof(struct aud));
/* aud 構造体を用意します。 */
audv->b_id = q_branchid;
audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
/* tpcall の実行 */
if (tpcall(svc_name,(char *)audv,sizeof(struct aud),
(char **)&audv,(long *)&audrl,0)== -1){
(void)fprintf (stderr, "%s service failed¥n %s:%s¥n",
svc_name, svc_name, audv->ermsg);
retc = -1;
}
else
(void)printf ("Branch %ld %s balance is $%.2f¥n",
audv->b_id, hdr_type, audv->balance);
. . .
例: 応答バッファのサイズ変更の確認
次のコード例は、tpcall() を呼び出した後、アプリケーションでバッファのサイズが変更されたかどうかを確認する方法を示しています。この例では、入力バッファと出力バッファは同じサイズでなければなりません。
コード リスト 6-2 応答バッファのサイズ変更の確認
char *svc, *idata, *odata;
long ilen, olen, bef_len, aft_len;
. . .
if (idata = tpalloc("STRING", NULL, 0) == NULL)
error
if (odata = tpalloc("STRING", NULL, 0) == NULL)
error
place string value into idata buffer
ilen = olen = strlen(idata)+1;
. . .
bef_len = olen;
if (tpcall(svc, idata, ilen, &odata, &olen, flags) == -1)
error
aft_len = olen;
if (aft_len > bef_len){ /* メッセージ・バッファのサイズが大きくなっている場合 */
if (idata = tprealloc(idata, olen) == NULL)
error
}
例:TPSIGRSTRT フラグを設定した同期メッセージの送信
以下のコード例は、bankapp の XFER サーバ・プロセスの一部である TRANSFER サービスに基づいています (bankapp は、BEA Tuxedo システムに提供されている銀行業務のサンプル・アプリケーションです)。TRANSFER サービスは、クライアントとして WITHDRAWAL および DEPOSIT サービスを呼び出します。アプリケーションはこの 2 つのサービスを呼び出すときに通信フラグを TPSIGRSTRT に設定して、トランザクションをコミットしやすいようにします。TPSIGRSTRT フラグは、シグナルの割り込みがあった場合に行う処理を指定します。通信フラグの詳細については、『BEA Tuxedo C リファレンス』の tpcall(3c) を参照してください。
コード リスト 6-3 TPSIGRSTRT フラグを設定した同期メッセージの送信
/* tpcall を実行して、振替元の口座から引き出します。 */
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb,
(long *)&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot withdraw from debit account", (FLDLEN)0);
tpfree((char *)reqfb);
}
...
/* tpcall を実行して、振替先口座に振替金額を移動します。 */
if (tpcall("DEPOSIT", (char *)reqfb, 0, (char **)&reqfb,
(long *)&reqlen, TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot deposit into credit account", (FLDLEN)0);
tpfree((char *)reqfb);
}
例:TPNOTRAN フラグを設定した同期メッセージの送信
以下のコード例は、トランザクション・モードではない通信呼び出しを示しています。この呼び出しは、リソース・マネージャに関連していないサービスに対して行われます。サービスがトランザクションに参加するとエラーになります。アプリケーションは、データベース accounts から取得した情報に基づいて生成された売掛金勘定レポート accrcv を出力します。
サービス・ルーチン REPORT は指定されたパラメータを解釈し、完了したレポートのバイト・ストリームを応答として送信します。クライアントは、tpcall() を使用して PRINTER サービスにバイト・ストリームを送信します。PRINTER は、クライアントに近いプリンタにバイト・ストリームを送信します。そして、応答が印刷されます。最後に、PRINTER サービスはハードコピーの印刷が終了したことをクライアントに通知します。
注記 TPNOTRAN と TPNOREPLY フラグを設定した非同期メッセージの送信では、同じ例を使用して非同期メッセージの呼び出しを行っています。
コード リスト 6-4 TPNOTRAN フラグを設定した同期メッセージの送信
#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* レポート用バッファ */
long r1len, r2len, r3len; /* レポート用の送信バッファ、最初の応答バッファ、
および 2 番目の応答バッファの長さ */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* レポート用の領域の割り当て */
leave application and exit program
(void)strcpy(rbuf,
"REPORT=accrcv DBNAME=accounts"); /* レポートのパラメータの送信 */
r1len = strlen(rbuf)+1; /* 要求の長さ */
start transaction
if (tpcall("REPORT", rbuf, r1len, &rbuf,
&r2len, 0) == -1) /* レポート印刷ストリームの取得 */
error routine
if (tpcall("PRINTER", rbuf, r2len, &rbuf,
&r3len, TPNOTRAN) == -1) /* プリンタへのレポートの送信 */
error routine
(void)printf("Report sent to %s printer¥n",
rbuf); /* プリンタの識別 */
terminate transaction
free buffer
アプリケーションを終了
}
注記 この例の error routine は、エラー・メッセージの出力、トランザクションの中止、割り当てられたバッファの解放、クライアントのアプリケーションからの分離、およびプログラムの終了が行われることを示しています。
例:TRNOCHANGE フラグを設定した同期メッセージの送信
以下のコード例では、最初に割り当てられたバッファ・タイプと同じタイプで応答メッセージを返す必要があることを示して、TPNOCHANGE 通信フラグを使用して厳密なタイプ・チェックを行う方法を示しています。この例では、REPORT というサービス・ルーチンを参照します。REPORT サービスは、例:TPNOTRAN フラグを設定した同期メッセージの送信でも使用されています。
このコード例では、クライアントが応答を VIEW 型バッファ rview1 で受信し、printf() 文でその内容を出力しています。厳密なタイプ・チェックのフラグ TPNOCHANGE が設定されているので、タイプ VIEW およびサブタイプ rview1 のバッファに応答が返されます。
厳密なタイプ・チェックを行うのは、REPORT サービス・サブルーチンでエラーが発生して、不適切なタイプの応答バッファが使用されることを防ぐためです。もう 1 つの理由は、依存関係にあるすべてのエリアで一貫していない変更が行わることを防ぐためです。たとえば、あるプログラマが REPORT サービスを変更してすべての応答を別の VIEW 形式で標準化したが、それを反映するためにクライアント・プロセスを変更しなかった場合などがあります。
コード リスト 6-5 TRNOCHANGE フラグを設定した同期メッセージの送信
#include <stdio.h>
#include "atmi.h"
#include "rview1.h"
main(argc, argv)
int argc;
char * argv[];
{
char *rbuf; /* レポート用バッファ */
struct rview1 *rrbuf; /* 応答バッファの通知 */
long rlen, rrlen; /* レポート用の送信バッファと
受信バッファの長さ */
if (tpinit((TPINIT *) tpinfo) == -1)
fprintf(stderr, "%s:failed to join application¥n", argv[0]);
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) { /* レポート用の領域の割り当て */
tpterm();
exit(1);
}
/* 応答バッファ用の領域の割り当て */
if (rrbuf = (struct rview1 *)tpalloc("VIEW", "rview1", sizeof(struct rview1)) ¥ == NULL{
tpfree(rbuf);
tpterm();
exit(1);
}
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts FORMAT=rview1");
rlen = strlen(rbuf)+1; /* 要求の長さ */
/* rview1 構造体でのレポートの取得 */
if (tpcall("REPORT", rbuf, rlen, (char **)&rrbuf, &rrlen, TPNOCHANGE) == -1) {
fprintf(stderr, "accounts receivable report failed in service call¥n");
if (tperrno == TPEOTYPE)
fprintf(stderr, "report returned has wrong view type¥n");
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
exit(1);
}
(void)printf("Total accounts receivable %6d¥n", rrbuf->total);
(void)printf("Largest three outstanding %-20s %6d¥n", rrbuf->name1, rrbuf->amt1);
(void)printf("%-20s %6d¥n", rrbuf->name2, rrbuf->amt2);
(void)printf("%-20s %6d¥n", rrbuf->name3, rrbuf->amt3);
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
}
非同期メッセージの送信
この節では、次の操作を行う方法について説明します。
ここで説明する非同期の処理は、ファンアウト並列処理と呼ばれます。クライアントの要求が複数のサービスに同時に分散 (つまり「ファンアウト」) されて処理が行われるからです。
このほかに、BEA Tuxedo システムでは、非同期処理としてパイプライン並列処理もサポートされています。この処理では、tpforward() 関数を使用して 1 つのサービスから別のサービスに処理が渡されます (転送されます)。tpforward() 関数については、サーバのコーディングを参照してください。
非同期要求の送信
tpacall(3c) 関数は、サービス要求を送信し、直ちに制御を戻します。tpacall() 関数の呼び出しには、次の文法を使用します。
int
tpacall(char *svc, char *data, long len, long flags)
次の表は、tpacall() 関数の引数を示しています。
表 6-2 tpacall( ) 関数の引数
tpacall() 関数は、svc パラメータに指定されたサービスに要求メッセージを送信し、直ちに制御を戻します。呼び出しが正常に終了すると、tpacall() 関数は整数値を返します。この値は、関連する要求に対する正しい応答にアクセスするための記述子として使用されます。tpacall() がトランザクション・モードで実行されている場合 (グローバル・トランザクションのコーディングを参照)、トランザクションのコミット時に未処理の応答が存在することはありません。つまり、あるトランザクションの範囲内では、要求ごとにその応答が返されるので、最終的には対応する応答を必ず受信することになります。 flags 引数に TPNOREPLY が設定されると、応答が必要ないことが tpacall() に通知されます。このフラグが設定されている場合、tpacall() の処理が正常に終了すると、応答記述子として 0 が返されます。以降の処理で、この値が tpgetrply() 関数に渡されると、この値は無効になります。プロセスがトランザクション・モードのときにこのフラグを正しく使用するためのガイドラインについては、グローバル・トランザクションのコーディングを参照してください。 エラーが発生した場合、tpacall() は -1 を返し、tperrno(5) には発生したエラー条件が返されます。tpacall() が返すエラー・コードの多くは、tpcall() が返すエラー・コードと同じです。この 2 つの関数のエラー・コードは、一方が同期呼び出し、もう一方が非同期呼び出しに基づいているという点が異なります。これらのエラーについては、エラーの管理を参照してください。 例: TPNOTRAN と TPNOREPLY フラグを設定した非同期メッセージの送信 以下のコード例は、tpacall() で TPNOTRAN と TPNOREPLY フラグを使用する方法を示しています。このコードは、例:TPNOTRAN フラグを設定した同期メッセージの送信のコードと同じです。ただし、ここで示すコードでは、PRINTER サービスからの応答は要求されていません。TPNOREPLY フラグはクライアントが応答を要求していないこと、TPNOTRAN フラグは PRINTER サービスが現在のトランザクションに参加しないことを示します。詳細については、エラーの管理を参照してください。 コード リスト 6-6 TPNOTRAN と TPNOREPLY フラグを設定した非同期メッセージの送信 例: 非同期要求の送信 以下のコード例は、銀行の総残高照会を行う非同期呼び出しを示しています。銀行業務アプリケーションのデータは、複数のデータベース・サイトに分散されているので、各サイトに対して SQL クエリを実行する必要があります。その場合、データベース・サイトごとに支店番号が 1 つ選択され、そのサイトに対して ABAL または TBAL サービスが呼び出されます。支店番号は実際の SQL クエリでは使用されませんが、この番号によって BEA Tuxedo システムが適切なサイトに要求を送ることができるようになります。次のコードでは、サイトごとに for ループが tpacall() を一度呼び出しています。 コード リスト 6-7 非同期要求の送信 非同期応答の受信 サービス呼び出しに対する応答は、tpgetrply(3c) 関数を呼び出すと非同期的に受信できます。tpgetrply() 関数は、tpacall() が以前に送信した要求に対する応答をキューから取り出します。 tpgetrply() 関数の呼び出しには、次の文法を使用します。 次の表は、tpgetrply() 関数の引数を示しています。 表 6-3 tpgetrply( ) 関数の引数#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* レポート用バッファ */
long rlen, rrlen; /* レポート用の送信バッファと受信バッファの長さ */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* レポート用の領域の割り当て */
error
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts");/* レポートのパラメータの送信 */
rlen = strlen(rbuf)+1; /* 要求の長さ */
start transaction
if (tpcall("REPORT", rbuf, rlen, &rbuf, &rrlen, 0)
== -1) /* レポート印刷ストリームの取得 */ error
if (tpacall("PRINTER", rbuf, rrlen, TPNOTRAN|TPNOREPLY)
== -1) /* プリンタへのレポートの送信 */
error
.. .
commit transaction
free buffer
アプリケーションを終了
}audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
for (i=0; i<NSITE; i++) {
/* aud 構造体を用意します。 */
audv->b_id = sitelist[i]; /* このフィールドでルーティングを行います。 */
/* tpacall の実行 */
if ((cd[i]=tpacall(sname, (char *)audv, sizeof(struct aud), 0))
== -1) {
(void)fprintf (stderr,
"%s: %s service request failed for site rep %ld¥n",
pgmname, sname, sitelist[i]);
tpfree((char *)audv);
return(-1);
}
}int
tpgetrply(int *cd, char **data, long *len, long flags)
デフォルトでは、この関数は、cd パラメータが参照する値に対応した応答を待ちます。応答を待っている間に、ブロッキング・タイムアウトが発生する場合があります。タイムアウトが発生するのは、tpgetrply() が失敗し、tperrno(5) に TPETIME が設定された場合です。ただし、flags パラメータに TPNOTIME が設定されている場合は除きます。
メッセージの優先順位の設定および取得
tpsprio(3c) と tpgprio(3c) の 2 つの ATMI 関数を使用して、メッセージ要求の優先順位を決定したり設定できます。この優先順位に従って、サーバがキューから要求を取り出します。つまり、最も優先順位の高い要求が最初に取り出されます。
この節では、次の内容について説明します。
メッセージの優先順位の設定
tpsprio(3c) 関数を使用すると、メッセージ要求の優先順位を設定できます。
tpsprio() ルーチンで優先順位を設定できるのは、1 つの要求だけです。つまり、tpcall() または tpacall() によって次に送信される要求、またはサービス・サブルーチンによって次に転送される要求だけです。
tpsprio() 関数の呼び出しには、次の文法を使用します。
int
tpsprio(int prio, long flags);
次の表は、tpsprio() 関数の引数を示しています。
表 6-4 tpsprio( ) 関数の引数
以下のコード例は、TRANSFER サービスから引用したものです。このコードでは、TRANSFER サービスはクライアントとして動作し、tpcall() を使用して WITHDRAWAL サービスに同期要求を送信しています。TRANSFER は tpsprio() を呼び出して WITHDRAWAL に対する要求メッセージの優先順位を上げます。また、TRANSFER のキューを待機した後で、WITHDRAWAL サービス (その後 DEPOSIT サービス) に対する要求がキューに格納されないようにします。 コード リスト 6-8 要求メッセージの優先順位の設定 メッセージの優先順位の取得 tpgprio(3c) を使用すると、メッセージ要求の優先順位を取得できます。 tpgprio() 関数の呼び出しには、次の文法を使用します。 要求元は、tpcall() または tpacall() 関数を呼び出した後に tpgprio() 関数を呼び出して、要求メッセージの優先順位を取得できます。要求元が関数を呼び出したが要求が送信されていない場合、関数は失敗して -1 が返され、tperrno(5) に TPENOENT が設定されます。tpgprio() の処理が成功すると、1 〜 100 の範囲内の整数値が返されます。100 が最も高い優先順位です。 tpsprio() 関数を使用して優先順位が明示的に設定されていない場合、メッセージの優先順として、要求を処理するサービス・ルーチンの優先順位が設定されます。アプリケーション内では、要求を処理するサービスの優先順位にデフォルト値の 50 が設定されます。ただし、システム管理者が別の値を指定している場合は除きます。 次のコード例は、非同期呼び出しによって送信されたメッセージの優先順位を確認する方法を示しています。 コード リスト 6-9 送信後の要求の優先順位の確認/* WITHDRAWAL サービス呼び出しの優先順位を上げます。 */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of withdraw¥n");
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb, (long *)
¥
&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0, "Cannot withdraw from debit account", ¥
(FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}int
tpgprio();#include <stdio.h>
#include "atmi.h"
main ()
{
int cd1, cd2; /* 呼び出し記述子 */
int pr1, pr2; /* 2 つの呼び出しに対する優先順位 */
char *buf1, *buf2; /* バッファ */
long buf1len, buf2len; /* バッファの長さ */
join application
if (buf1=tpalloc("FML", NULL, 0) == NULL)
error
if (buf2=tpalloc("FML", NULL, 0) == NULL)
error
populate FML buffers with send request
if ((cd1 = tpacall("service1", buf1, 0, 0)) == -1)
error
if ((pr1 = tpgprio()) == -1)
error
if ((cd2 = tpacall("service2", buf2, 0, 0)) == -1)
errorif ((pr2 = tpgprio()) == -1)¥
error
if (pr1 >= pr2) { /* 呼び出しの優先順位に基づいて、tpgetreply の順序を設定します。 */
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
}
else {
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
}
. . .
}
![]() |
![]() |
![]() |
![]() |
||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |