コネクションレスモードサービスはトランザクション処理アプリケーションなどの短期の要求および応答対話に適しています。データは自己内包型ユニットで転送され、複数のユニット間の論理的な関連は必要ありません。
トランスポートのユーザーはデータ転送を行う前に XTI/TLI エンドポイントの初期化を行う必要があります。まず t_open(3NSL) を使用して適切なコネクションレスサービスプロバイダを選択し、t_bind(3NSL) を使用してその識別アドレスを確定します。
プロトコルオプションのネゴシエーションには t_optmgmt(3NSL) を使用します。コネクションモードサービス同様、各トランスポートプロバイダがサポートされているオプションを任意に指定します。オプションのネゴシエーションはプロトコル固有の処理です。例 3-1 はサーバーが着信クエリーの待ち状態から、その後、処理、応答を行う例です。例ではサーバーの定義および初期化シーケンスのコードも見ることができます。
#include <stdio.h> #include <fcntl.h> #include <xti.h> /* TLI アプリケーションは <tiuser.h> を使用*/ #define SRV_ADDR 2 /* サーバーの既知アドレス*/ main() { int fd; int flags; struct t_bind *bind; struct t_unitdata *ud; struct t_uderr *uderr; extern int t_errno; if ((fd = t_open("/dev/exmp", O_RDWR, (struct t_info *) NULL)) == -1) { t_error("unable to open /dev/exmp"); exit(1); } if ((bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == (struct t_bind *) NULL) { t_error("t_alloc of t_bind structure failed"); exit(2); } bind->addr.len = sizeof(int); *(int *)bind->addr.buf = SRV_ADDR; bind->qlen = 0; if (t_bind(fd, bind, bind) == -1) { t_error("t_bind failed"); exit(3); } /* * TLI インタフェースアプリケーションでは以下のコードが必要です。 * XTI アプリケーションでは必要ありません。 *------------------------------------------------------------ * バインドされたアドレスの検査 * * if (bind -> addr.len != sizeof(int) || * *(int *)bind->addr.buf != SRV_ADDR) { * fprintf(stderr, "t_bind bound wrong address¥n"); * exit(4); * } * --------------------------------------- */
サーバーは t_open(3NSL) を使用して、任意のトランスポートプロバイダとのトランスポートエンドポイントを確立します。各プロバイダには関連付けられたサービスタイプがあるため、ユーザーは適切なトランスポートプロバイダファイルを開くことにより特定のサービスを選択できます。このコネクションレスモードサーバーは 3 つ目の引数を NULL に設定すると t_open(3NSL) により返されるプロバイダの特性を無視します。トランザクションサーバーはトランスポートプロバイダが以下の特性を持っていると判断します。
トランスポートアドレスは整数値であり各ユーザー固有のものである。
トランスポートプロバイダは T_CLTS サービスタイプをサポートしている (コネクションレストランスポートサービスまたはデータグラム)。
トランスポートプロバイダはプロトコル固有オプションを必要としない。
コネクションレスサーバーはアクセスの可能性のあるすべてのクライアントがサーバーにアクセスできるようエンドポイントにトランスポートアドレスをバインドします。t_alloc(3NSL) により t_bind 構造体が割り当てられ、アドレスの buf および len フィールドが設定されます。
コネクションモードサーバーとコネクションレスモードサーバーの違いの 1 つは、コネクションレスモードサービスの場合は t_bind 構造体の qlen フィールドが 0 であるという点です。待機状態の接続要求が存在しないということです。
XLI/TLI インタフェースはコネクションモードサービス内でトランスポートコネクションの確立を行いながら 2 ユーザー間において固有のクライアントサーバー関係を定義します。このような関係はコネクションレスモードサービスでは存在しません。
TLI では t_bind(3NSL) より返されるバインドされたアドレスが指定されているアドレスと同一であることを保証するため、サーバーによるアドレスのチェックを必要としています。また要求されたアドレスがビジーの場合、t_bind(3NSL) はエンドポイントに、個別の使用されていないアドレスをバインドすることが可能です。
ユーザがトランスポートエンドポイントにアドレスをバインドさせた後、データグラムはエンドポイントを介し送受信が可能になります。各送信メッセージは宛先ユーザーのアドレスとともに送信されます。また XTI/TLI はデータユニット転送へのプロトコルオプションの指定を可能にします (たとえば、transit delay )。各トランスポートプロバイダはオプションセットをデータグラム上で定義します。データグラムが宛先ユーザーに渡された時点で、関連付けられたプロトコルオプションも渡されます。
例 3-2 は、コネクションレスモードサーバーのデータ転送フェーズの例です。
if ((ud = (struct t_unitdata *) t_alloc(fd, T_UNITDATA,T_ALL)) == (struct t_unitdata *) NULL) { t_error("t_alloc of t_unitdata struct failed"); exit(5); } if ((uderr = (struct t_uderr *) t_alloc(fd, T_UDERROR, T_ALL)) == (struct t_uderr *) NULL) { t_error("t_alloc of t_uderr struct failed"); exit(6); } while(1) { if (t_rcvudata(fd, ud, &flags) == -1) { if (t_errno == TLOOK) { /* 前に送られたデータグラムのエラ−*/ if(t_rcvuderr(fd, uderr) == -1) { exit(7); } fprintf(stderr, "bad datagram, error=%d¥n", uderr->error); continue; } t_error("t_rcvudata failed"); exit(8); } /* * Query() が要求の処理を行い、応答を ud->udata.buf に格納、 * ud->udata.len が設定される。 */ query(ud); if (t_sndudata(fd, ud) == -1) { t_error("t_sndudata failed"); exit(9); } } } /* 引数の使用 */ void query(ud) struct t_unitdate *ud; { /* 簡略化のため関数の切り口のみ */ }
データグラムのバッファー化を行うにはサーバーはまず以下の形式を持つ t_unitdata 構造体の割り当てを行う必要があります。
struct t_unitdata { struct netbuf addr; struct netbuf opt; struct netbuf udata; }
addr は入力されるデータグラムの送信元アドレスと、出力されるデータグラムの宛先アドレスを保持します。opt はデータグラムのプロトコルオプションを保持します (プロトコルオプションがある場合)。udata はデータを保持します。addr、opt、および udata フィールドはいかなる入力値にも対応できるよう、十分なバッファーを割り当てておく必要があります。 これを確実に行うため、t_alloc(3NSL) の T_ALL 引数は各 netbuf 構造体の maxlen フィールドを必要に応じ設定します。この例ではプロバイダはプロトコルオプションをサポートしていないため、opt の netbuf 構造体の maxlen は 0 に設定されています。またサーバーはデータグラムエラー用に t_uderr 構造体の割り当てを行います。
トランザクションサーバーは無限ループ状態でクエリーの受信、処理およびクライアントへの応答を行います。最初に次のクエリーを受信するため t_rcvudata(3NSL) を呼び出します。t_rcvudata(3NSL) はデータグラムが入力されるまでブロックし、クエリーを戻します。
t_rcvudata(3NSL) の 2 番目の引数はデータグラムのバッファーを行うための t_unitdata 構造体を特定しています。
3 番目の引数 flags は整数型変数を指し、t_rcvudata(3NSL) からの戻り値にユーザーの udata バッファーがデータグラムを格納するだけの大きさが確保されていないことを示す T_MORE
が設定されることが可能です。
上の事態が発生した場合、t_rcvudata(3NSL) の次の呼び出しは残りのデータグラムを取り戻します。t_alloc(3NSL) が最大サイズのデータグラムを格納するのに十分な大きさの udata バッファーを割り当てるため、このトランザクションサーバーは flags のチェックを行う必要がありません。これは t_rcvudata(3NSL) のみに当てはまり、他の受信プリミティブには適用されません。
データグラムが受信された場合、トランザクションサーバーは要求の処理を行うため、query ルーチンを呼び出します。このルーチンは ud が指す構造体に応答を格納し、ud->udata.len を応答のバイト数に合わせ設定します。t_rcvudata(3NSL) により戻される ud->addr の送信元アドレスは t_sndudata(3NSL) の宛先アドレスです。応答の準備が完了した時点で、t_sndudata(3NSL) が呼び出され、応答はクライアントに送信されます。
トランスポートプロバイダが t_sndudata(3NSL) によって送られたデータグラムの処理を行えない場合、ユーザーにユニットデータエラーイベント T_UDERR を戻します。このイベントにはエラーを識別するため、データグラムの宛先アドレスとオプション、プロトコル固有エラー値が含まれます。データグラムのエラーはプロトコル固有 (専用) のものです。
ユニットデータエラーイベントは指定された宛先へのデータグラム転送の成功または失敗を必ずしも示しません。コネクションレスサービスはデータの確実な転送を保証していないことを覚えておいてください。
別のデータグラムの受信を行おうとした場合トランザクションサーバーにはエラーが通知されます。この場合、t_rcvudata(3NSL) は失敗し、t_errno は TLOOK に設定されます。TLOOK が設定されている場合、発生の可能性のあるイベントは T_UDERR のみであることから、サーバーはイベントの取り出しを行うため t_rcvudata(3NSL) を呼び出します。t_rcvuderr(3NSL) の 2 番目の引数は事前に割り当てられている t_uderr 構造体です。この構造体は t_rcvuderr(3NSL) によって格納され、次の形式です。
struct t_uderr { struct netbuf addr; struct netbuf opt; t_scalar_t error; }
addr および opt は不正なデータグラムで指定された宛先アドレスおよびプロトコルオプションを特定し、error はプロトコル固有のエラーコードです。トランザクションサーバーはエラーコードを出力し、処理を継続します。