ネットワークインタフェース

データ転送

接続が確立された後、サーバーおよびクライアントは t_snd(3NSL) および t_rcv(3NSL) を使用してデータの転送が行えるようになります。XTI/TLI はこれ以降、クライアントおよびサーバーを区別なく扱います。どちらのユーザーもデータの送信、受信、および接続の解放が行えます。

トランスポート接続上のデータのクラスには以下の 2 種類があります。

  1. 普通データ (Normal data)

  2. 優先データ (Expedited data)

優先データは緊急度の高いデータに使用されます。優先データの正確な意味論はトランスポートプロバイダにより異なります。すべてのトランスポートプロトコルが優先データをサポートしているわけではありません (t_open(3NSL) を参照)。

ほとんどのコネクション型モードプロトコルはバイトストリームによってデータの転送を行います。「バイトストリーム」では接続上で送信されるデータにメッセージ境界を与えていません。トランスポートプロトコルによってはトランスポート接続上でメッセージ境界を持っているものもあります。このサービスは XTI/TLI によってサポートされていますが、プロトコルに依存しないソフトウェアで使用することは避けてください。

メッセージ境界は t_snd(3NSL) および t_rcv(3NSL)T_MORE フラグによって発行されます。トランスポートサービスデータユニット (TSDU) と呼ばれるメッセージは、2 つのトランスポートユーザー間を個別のユニットとして転送を行うことが可能です。メッセージの最大サイズは基本のトランスポートプロトコルにより定義されます。メッセージサイズは t_open(3NSL) または t_getinfo(3NSL) で得られます。

メッセージは複数のユニットで送信できます。そのためには (複数のユニットの) 最後のメッセージ以外のすべての t_snd(3NSL) 呼び出しに T_MORE フラグを設定します。フラグは現在と次の t_snd(3NSL) 呼び出しは論理ユニットであることを指定します。最後のメッセージで T_MORE フラグを外すことにより論理ユニットの終わりを指定します。

同様に論理ユニットを複数のユニットで送信することも可能です。もし t_rcv(3NSL)T_MORE フラグが設定された状態で戻された場合、ユーザーはメッセージの続きを受信するため再び t_rcv(3NSL) を呼び出す必要があります。ユニットの最後のメッセージは T_MORE が設定されていない t_rcv(3NSL) の呼び出しによって認識することが可能です。

T_MORE フラグは XTI/TLI によるデータのパッケージ方法および遠隔ユーザーへのデータの配信方法については指定しません。各トランスポートプロトコルおよび各プロトコルの実装方法によりデータのパッケージおよび配信を異なる方法で行えます。

たとえば、ユーザーが t_snd(3NSL) への 1 回の呼び出しで完全なメッセージを送った場合、トランスポートプロバイダが受信ユーザーへ 1 つのユニットでデータの配信を行う保証はありません。同様に、2 つのユニットにより送信されたメッセージは遠隔トランスポートユーザーへ 1 つのユニットとして配信される可能性があります。

トランスポートによりサポートされている場合、メッセージ境界は T_MORE の値を t_snd(3NSL) に設定し、t_rcv(3NSL) の後にテストを行うことでのみ保持されます。これにより受信ユーザーは送られたときと同一の内容とメッセージ境界のメッセージを見ることが保証されます。

クライアント

例のサーバーはクライアントへトランスポート接続を使用しログファイルを転送しています。クライアントはそのデータを受信し、標準出力ファイルへ書き込みます。クライアントとサーバーではメッセージ境界を持たないバイトストリームインタフェースが使用されています。クライアントは以下によりデータを受信します。

while ((nbytes = t_rcv(fd, buf, nbytes, &flags))!= -1){
   if (fwrite(buf, 1, nbytes, stdout) == -1) {
      fprintf(stderr, "fwrite failed¥n");
      exit(5);
   }
}

クライアントは受信データを受け取るため繰り返し t_rcv(3NSL) を呼び出します。t_rcv(3NSL) はデータが着信するまでブロックします。t_rcv(3NSL) はデータの量に合わせ nbytes のデータを buf に格納しバッファーに書き込んだバイト数を戻します。クライアントは標準出力ファイルへデータを書き込み、処理を継続します。データ転送ループは t_rcv(3NSL) が失敗した段階で終了します。t_rcv(3NSL) は正常型解放または切断要求が着信した時点で失敗します。何らかの理由で fwrite(3C) が失敗した場合、クライアントは終了し、トランスポートエンドポイントは閉じられます。トランスポートエンドポイントがデータ転送中に閉じられた場合 (exit(2) または t_close(3NSL) によって)、接続は中止され遠隔ユーザーは接続の切断要求を受信します。

サーバー

サーバーは子プロセスを生成しクライアントへデータを転送することによりデータ転送を管理します。親プロセスは接続要求の待機を行うためループを継続します。例 3-7 で示すとおり、サーバーでは子プロセスの生成を行うため run_server が呼び出されます。


例 3-7 ループバックおよび待機を行うための子プロセスの生成

connrelease()
{
   /*ここで必要なため conn_fd  はグローバル*/
   if (t_look(conn_fd) == T_DISCONNECT) {
      fprintf(stderr, "connection aborted¥n");
      exit(12);
   }
   /*上記以外の場合、正常型解放を要求-通常終了*/
   exit(0);
}
run_server(listen_fd)
int listen_fd;
{
   int nbytes;
   FILE *logfp;                    /*ログファイルへのファイルポインタ*/
   char buf[1024];

   switch(fork()) {
   case -1:
      perror("fork failed");
      exit(20);
   default:									/*親*/
      /* conn_fd を閉じ、戻って再び待機する*/
      if (t_close(conn_fd) == -1) {
         t_error("t_close failed for conn_fd");
         exit(21);
      }
      return;
   case 0:                        /*子*/
      /* listen_fd を閉じ、サービスを行う */
      if (t_close(listen_fd) == -1) {
         t_error("t_close failed for listen_fd");
         exit(22);
      }
      if ((logfp = fopen("logfile", "r")) == (FILE *) NULL) {
         perror("cannot open logfile");
         exit(23);
      }
      signal(SIGPOLL, connrelease);
      if (ioctl(conn_fd, I_SETSIG, S_INPUT) == -1) {
         perror("ioctl I_SETSIG failed");
         exit(24);
      }
      if (t_look(conn_fd) != 0){      /*切断するかどうか*/
         fprintf(stderr, "t_look: unexpected event¥n");
         exit(25);
      }
      while ((nbytes = fread(buf, 1, 1024, logfp)) > 0)
         if (t_snd(conn_fd, buf, nbytes, 0) == -1) {
            t_error("t_snd failed");
            exit(26);
         }

フォーク後、親プロセスは待機のメインループへ戻ります。子プロセスは新たに確立されたトランスポート接続を管理します。フォークが失敗した場合、exit(2) が両方のトランスポートエンドポイントを閉じ、クライアントに接続の切断要求を送り、クライアントの t_connect(3NSL) 呼び出しは失敗します。

サーバーの処理はログファイルから一度に 1024 バイトを読み込み、クライアントに t_snd(3NSL) を使用して送ります。buf はデータバッファーの開始点を指し、nbytes は送信するデータのバイト数を指定します。4 つ目の引数には、0 または以下の 2 つのオプションフラグを指定することが可能です。

この例ではどちらのフラグもサーバーによって設定されていません。

ユーザーがトランスポートプロバイダをデータであふれさせた場合、トランスポートから十分なデータが取り除かれるまで t_snd(3NSL) がブロックを行います。

t_snd(3NSL) は接続の切断要求を捜しません (接続が切断したことを表示)。接続が中止された場合、データが失われる可能性があるためサーバーへ通知する必要があります。1 つの解決法は、各 t_snd(3NSL) 呼び出しの前、または t_snd(3NSL) 失敗の後に、着信するイベントのチェックを行うよう t_look(3NSL) を呼び出すことです。例ではより整理された手法を使用しています。ioctl(2)I_SETSIG によって指定されているイベントが発生した場合にユーザー要求をシグナルにします。 streamio(7I) のマニュアルページを参照してください。S_INPUT によりエンドポイント conn_fd に入力が着信した場合にシグナルをユーザープロセスへ送信します。接続の切断要求が着信した場合、シグナルを検知するルーチン (connrelease) がエラーメッセージを出力し、終了します。

サーバーが t_snd(3NSL) および t_rcv(3NSL) 呼び出しを交互に行う場合、着信する接続の切断要求を認識するために、t_rcv(3NSL) を使用することが可能です。