Netcompo WAN制御 説明書
目次 前ページ次ページ

上へ第8章 環境構築7 TLI編

8.3 X.25 API

ここでは、X.25手順を使用し、アプリケーションが他システムと通信を行う場合のインタフェースについて説明します。

X.25手順使用時のアプリケーション・インタフェースは、TLIのコネクション・モードに変換されており、アプリケーションは、TLIを使用する場合とほぼ同じ方法でネットワークを使用し、通信することができます。
ただし、X.25手順独自の機能も追加されており、以降、本インタフェースをX.25-TLIと記述し、TCP/IP手順でのTLIと区別します。

8.3.1 X.25-TLIの概要

X.25-TLIインタフェースは、アプリケーションに対して、TLIと同様のインタフェースを提供することを目的としています。
このため、ネットワーク・プロバイダ・インタフェースをトランスポート・プロバイダ・インタフェースに変換しています。

図8.7にX.25-TLIの概要を示します。

image

図8.7 X.25-TLIの概要

X.25インタフェース・プロバイダは、上位にネットワーク・レイヤ・インタフェースを提供します。
これをTLI変換モジュールが、トランスポート・レイヤ・インタフェースに変換することにより、ユーザに対してX.25-TLIを提供します。
したがって、ネットワーク層のサービスはトランスポート層のサービスにマッピングされ、ネットワーク・エンドポイント(ネットワーク・コネクションの端点)はトランスポート・エンドポイントにマッピングされています。

以降の説明では、基本的にネットワーク層がトランスポート層にマッピングされていることを前提に、トランスポートのインタフェース用語で説明します。

図8.8にマッピングのモデルを、また、表8.6に対応する用語を示します。

image

図8.8 マッピングモデル

表8.6 トランスポート層用語に対応するネットワーク層用語一覧

本文中の記述

マッピング対象

意味

トランスポート・エンドポイント

ネットワーク・エンドポイント

1つのトランスポート・エンドポイントは、1つのネットワーク・エンドポイントに対応しています。
ネットワーク・エンドポイントは、ネットワーク・コネクションの端点です。このため、トランスポート・エンドポイントにより、ネットワーク・コネクションが識別されます。

トランスポート・コネクション

ネットワーク・コネクション

X.25で独立の通信を行える最小単位であり、VCまたはPVCに対応します。

トランスポート・プロバイダ

コンバート・モジュール

+X.25プロバイダ

X.25プロバイダは、ネットワーク・インタフェースを提供しますが、このインタフェースは、コンバート・モジュールによりトランスポート・インタフェースに変換されます。したがって、コンバート・モジュール + X.25プロバイダが、1つのトランスポート・プロバイダと見なすことができます。

8.3.2 X.25-TLIのサービス・モード

X.25-TLIには、以下の2つのモードがあります。

VCモードは、X.25手順のVCモードを使用するものであり、PVCモードは、X.25手順のPVCモードを使用するものです。

8.3.2.1 VCモード・サービス

VCモード・サービスは、以下の4つのフェーズによって特徴づけられます。

◆ローカル管理

ローカル管理フェーズは、X.25-TLIユーザとトランスポート・プロバイダ間のローカル動作を定義します。
前述のとおり、トランスポート・プロバイダは、X.25プロバイダとコンバート・モジュールとみなします。例えばユーザは、図8.9で示されるように、トランスポート・プロバイダとの通信のチャネルを確立する必要があります。X.25-TLIユーザとトランスポート・プロバイダ間の各チャネルは、通信の一意のエンドポイントであり、トランスポート・エンドポイントと呼ばれます。

t_open(3N)関数によって、ユーザはVCモード・サービスを行う特定のトランスポート・プロバイダを選択することができ、さらにそのトランスポート・エンドポイントを確立することができます。

image

図8.9 ユーザとプロバイダ間のチャネル

各ユーザにとって必要なもう1つのローカル機能は、トランスポート・プロバイダの実体を確立することです。各ユーザは、ネットワーク・アドレスによって識別されます。より正確に言えば、ネットワーク・アドレスは、各トランスポート・エンドポイントと関連しており、1つのユーザ・プロセスがいくつかのトランスポート・エンドポイントを管理できます。

VCモード・サービスでは、あるユーザは、別のユーザのアドレスを指定することによって、そのユーザとのコネクションを要求します。ネットワーク・アドレスの構造は、ネットワーク・プロバイダのアドレス形式によって定義され、各トランスポート・エンドポイントに対するアドレスの割当ては、t_bind(3N)を使用して行います。

t_openやt_bindの他にも多くの関数がローカル動作をサポートするために使用できます。表8.7にX.25-TLIのすべてのローカル管理関数を要約します。

表8.7 X.25-TLIのローカル管理関数

関数

説明

t_alloc

トランスポート・インタフェースのデータ構造体を割り当てる。

t_bind

ネットワーク・アドレスをトランスポート・エンドポイントに結びつける。

t_close

トランスポート・エンドポイントをクローズする。

t_error

トランスポート・インタフェースのエラー・メッセージを表示する。

t_free

t_allocを使用して割り当てた構造体を解放する。

t_getinfo

特定のトランスポート・プロバイダに関連するパラメタのセットを返す。

t_getstate

トランスポート・エンドポイントの状態を返す。

t_look

トランスポート・エンドポイント上の現在の事象を返す。

t_open

選択されたトランスポート・プロバイダに接続されるトランスポート・エンドポイントを確立する。

t_optmgmt

トランスポート・プロバイダと、プロトコル固有オプションをネゴシエートする。

t_sync

トランスポート・プロバイダと、トランスポート・エンドポイントの同期をとる。

t_unbind

ネットワーク・アドレスをトランスポート・エンドポイントから切り離す。

◆コネクションの確立

コネクション確立フェーズでは、図8.10に示されるように、各ユーザが相互間でコネクションまたは、バーチャル・サーキットを生成することができます。

image

図8.10 トランスポート・コネクション

このフェーズは、各X.25-TLIユーザ間のクライアント/サーバ関係によって表すことができます。
通常、一方のユーザがサーバとなり、ユーザ・グループに対してある種のサービスを宣言して、これらのユーザからの要求を待ちます。
各クライアントは、サービスを必要とするときに、サーバが宣言したネットワーク・アドレスを使用して、自分とサーバを接続しようとします。

t_connect(3N)関数は、接続要求を開始します。t_connect関数の引数の1つであるネットワーク・アドレスは、クライアントがアクセスするサーバを指定します。
サーバは、t_listen(3N)によってクライアントからの接続要求が送られてきたことを知り、t_accept(3N)を呼び出してサービスへのアクセスを求めるクライアントの要求を受け付けます。
要求が受け付けられると、トランスポート・コネクション(ネットワーク・コネクション)が確立されます。

表8.8にX.25-TLIのコネクションを確立するために使用できるすべての関数を要約します。

表8.8 X.25-TLIのコネクションを確立するための関数

関数

説明

t_accept

トランスポート・コネクションの要求を受け付ける。

t_connect

指定されたあて先のX.25-TLIユーザとのコネクションを確立する。

t_listen

他のX.25-TLIユーザからの接続要求を検索、受信する。

t_rcvconnect

t_connectが非同期モードで呼び出されていた場合には、コネクションの確立を終了する。

◆データ転送

データ転送フェーズでは、ユーザは確立されたコネクション上での双方向のデータ転送を行うことができます。

t_snd(3N)とt_rcv(3N)の2つの関数は、このコネクション上でのデータの送信と受信を行います。
ユーザが送信するすべてのデータは、送信した順番どおりに、このコネクションの他端のユーザに送信されます。

表8.9にX.25-TLIのデータ転送を行うために使用できるすべての関数を要約します。

表8.9 X.25-TLIのデータ転送を行うための関数

関数

説明

t_rcv

トランスポート・コネクションを通して到着したデータを受信する。

t_snd

確立されたトランスポート・コネクションを通してデータ送信する。

◆コネクションの解放

コネクション解放フェーズでは、ユーザは確立されたコネクションを切断することができます。
ユーザは、通信を終了させることを決定した時に、プロバイダがトランスポート・コネクションを解放するように要求することができます。

t_snddis(3N)関数は強制切断を開始し、t_rcvdis(3N)関数は強制切断のための着信指示を処理します。
この強制切断により、すでに送信され、他方のトランスポート・ユーザにまだ到着していないデータは、すべてトランスポート・プロバイダによって廃棄されます。

表8.10にX.25-TLIのコネクション解放を行うために使用できるすべての関数を要約します。

表8.10 X.25-TLIのコネクションを解放するための関数

関数

説明

t_rcvdis

理由コードやユーザ・データを含めて、打ち切られたコネクションの指示を返す。

t_snddis

コネクションを強制終了するか、または接続要求を拒否する。

8.3.2.2 PVCモード・サービス

PVCモード・サービスは、VCモード・サービスと同様に、4つのフェーズによって特徴づけられます。
しかし、PVCモード・サービスは相手固定接続であり、ネットワークへの加入時に通信する相手システムは固定的に決定されるため、以下の内容が異なります。

ユーザは、PVCモード・サービスで通信を行う場合、通信を行う双方において、必ずコネクト動作(t_connect関数)を行わなければなりません。したがって、X.25-TLIのVCモード・サービスがサポートする関数のうち、次の関数はPVCモード・サービスではサポートしません。

8.3.2.3 状態の遷移

X.25-TLIは、次の2つの構成要素を持ちます。

状態遷移規則は、状態情報および事象の処理に基づいています。これらの事象には、ユーザが発生させたライブラリ呼び出しだけでなく、プロバイダが発生させた事象指示も含まれます。

注意:
X.25-TLIのユーザは、このインタフェースを用いてソフトウェアを記述する前に、起こりうるすべての状態遷移について理解しておく必要があります。

8.3.3 VCモード・サービスの概説

ここでは、X.25-TLIのVCモード・サービスについて、前節で説明したようなクライアント/サーバのモデルの例を用いて説明します。また、2つのプログラミング例を用いて、VCモード・サービスの重要な概念を提示します。これらの例は関連しており、一つの例はクライアントがどのようにサーバへのコネクションを確立し、そのサーバと通信するかを示し、もう一つの例は対となるサーバ側を示します。ここで示す例は、「プログラム例」でその全体が示されます。

これらの例では、クライアントは、サーバ・プロセスとのコネクションを確立します。サーバは、その時にクライアントにファイルを転送します。クライアントはサーバからのデータを受信し、そのデータをクライアントの標準出力ファイルに書き込みます。

8.3.3.1 ローカル管理

クライアントとサーバが、トランスポート・コネクションを確立するための条件として、両者は各自で、まずt_openを使用してトランスポート・プロバイダへのローカル・チャネル(トランスポート・エンドポイント)を確立し、次にt_bindを使用して自分の実体(トランスポート・エンドポイントを他のトランスポート・エンドポイントと識別するために割り当てられるアドレス)を確立する必要があります。

X.25-TLIユーザは、t_openにより1つのトランスポート・エンドポイントを設定します。実際は、トランスポート・エンドポイントがネットワーク・エンドポイントにマッピングされているため、X.25プロバイダにネットワーク・エンドポイントを設定することになります。
t_openは、このネットワーク・エンドポイントに関する以下の情報を、<tiuser.h>で定義されているt_info構造体の形式でユーザに返します。

addr

ネットワーク・アドレス情報(ネットワーク・アドレス構造体)のサイズ。

options

トランスポート・ユーザとトランスポート・プロバイダの間で引き渡されるプロトコル固有オプションの最大バイト数。

tsdu

トランスポート・コネクションを通じて送信されるメッセージの最大サイズ。X.25-TLIでは、コネクションの確立前は、デフォルト値の4096バイトが設定され、コネクション確立後は、ネゴシエーション後の送信パケットサイズが設定されます。

etsdu

トランスポート・コネクションを通じて送信される優先メッセージの最大サイズ。

connect

コネクション確立中にユーザ間で引き渡されるユーザ・データの最大バイト数。

discon

コネクションの解放中にユーザ間で引き渡されるユーザ・データの最大バイト数。

servtype

トランスポート・プロバイダによりサポートされるサービスのモード。X.25-TLIでは、以下のサービス・タイプとなります。
   T_COTS  現在のサービスは、コネクション型サービスです。
注意:
t_openは、トランスポート・エンドポイントに関連付けられているデフォルトのプロバイダ特性を返します。しかし、特性の一部はエンドポイントがオープンされた後で変更されることがあります。例えば、オプションの処理を行った場合、特性の値が変更されることがあります。(オプションの処理については、「8.3.6.2 X.25プロトコル上の制限を受ける関数」のt_optmgmt関数を参照してください。)t_getinfoを呼び出すことにより、トランスポート・エンドポイントの現在の特性が検索できます。

ユーザは、選択したトランスポート・プロバイダとのトランスポート・エンドポイントを確立した後、自分の実体を確立しなければなりません。前述のように、t_bindはネットワーク・アドレスをトランスポート・エンドポイントにバインドすることにより、これを達成します。さらにサーバ側では、この関数はトランスポート・プロバイダに対して、その用途が接続指示の受付に使用されるものであるか、あるいは接続要求の呼び出しに使用されるものであるかを指示することができます。

オプションの関数t_optmgmt(3N)も、ローカル管理フェーズ中に使用できます。ユーザは、この関数によってプロトコル・オプションの値をトランスポート・プロバイダとネゴシエートすることができます。プロトコルごとに、ネゴシエート可能なプロトコル・オプション群が独自に規定されています。このオプションはプロトコル固有の性質のため、この機能を使用できるのは特定のプロトコル環境用に作成されたアプリケーションだけです。

◆クライアント

ここでは、クライアントとサーバのローカル管理の例を使用して、これらの機能を詳細に説明します。次に示すのは、クライアントのプログラムが必要とする定義と、必要なローカル管理手順です。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
#define RCVDATASIZE 1024
extern int      t_errno;
void
main(int argc, char *argv[])
{
        int                   fd = 0;
        char                  netid[] = "x25";
        struct netconfig      *config = NULL;
        struct nd_hostserv    hostserv;
        struct nd_addrlist    *destaddr = NULL;
        struct t_call         *call = NULL;
        int                   nbytes = 0;
        char                  rcvbuf[RCVDATASIZE];
        int                   rcv_flags = 0;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
        /*
         *      t_open()
         */
        if(( fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }
        /*
         *      t_bind()
         */
        if( t_bind( fd, NULL, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 3 );
        }

t_openの第1引数は、VCモード・サービスを提供するX.25プロトコルを識別するファイル・システム・ノードのパス名です。この例で示す/dev/tpi/nlcovcは、総称的なコネクション型X.25手順のVCモードを識別する特殊ファイル(デバイス)名です。このデバイスは、第2引数であるO_RDWRオープン・フラグの指定に応じて、読み出しおよび、書き込み用にオープンされます。
第3引数は、トランスポート・プロバイダのサービス特性をユーザに返すために使用されます。プロトコルに依存しないソフトウェアを書く時に役に立ちます。ここに示す例のクライアントとサーバはこの情報を無視し、トランスポート・プロバイダが以下の特性を持っていると仮定しています。

これらの特性は、ユーザによっては必要とされないので、t_openへの第3引数にはNULLが指定されています。
t_openの戻り値は、以後のすべてのX.25-TLIインタフェース関数の呼出しによって使用される、トランスポート・エンドポイントに対する識別子です。この識別子は、実際にはトランスポート・プロトコル・ファイルをオープンすることにより得られるファイル記述子です。(open(2)を参照してください。)
トランスポート・エンドポイントが生成された後、クライアントはエンドポイントにアドレスを割り当てるために、t_bindを呼び出します。第1引数は、トランスポート・エンドポイントを指定します。第2引数は、ユーザがエンドポイントに割り当てようとしているアドレスを指定します。第3引数は、X.25-TLIにおいては意味がありません。

サーバのトランスポート・エンドポイントに関連付けられたアドレスは、すべてのクライアントがサーバにアクセスするために使用するアドレスであるため重要です。しかし、一般のクライアントは他のプロセスからアクセスされることはないので、自分のアドレスを気にする必要はありません。この例も同様であるため、t_bindの第2引数にはNULLが設定されています。第2引数にNULLが設定されると、トランスポート・プロバイダは適当なラインを選択します。

注意:
クライアントにおいて、t_bindの第2引数にアドレスを指定する場合、qlenには必ず0を設定してください。

t_openかt_bindのどちらかが失敗すると、プログラムは、t_error(3N)を呼び出して適切なエラー・メッセージを標準エラー出力に出します。X.25-TLI関数が失敗すると、グローバル整数t_errnoに該当するトランスポート・エラー値が設定されます。X.25-TLIにおけるエラー値は、<tiuser.h>で定義されており、t_errorはt_errnoの値に応じたエラー・メッセージを出力します。この関数は、errnoの値に基づいてエラー・メッセージを出力するperror(3C)に類似しています。トランスポート機能に関連したエラーが、システム・エラーである場合、t_errnoにはTSYSERRが設定され、errnoには<niuser.h>で定義された、プロトコル固有のエラー値が設定されます。

◆サーバ

この例のサーバは、通信を開始する前に、同様なローカル管理の手順を行う必要があります。サーバは、接続指示の受付待ちをするためのトランスポート・エンドポイントを確立しなければなりません。必要な定義およびローカル管理手順を次に示します。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <signal.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
extern int      t_errno;
int             conn_fd;
#define RCVDATASIZE 1024
void  run_server( int listen_fd );
void  connrelease( int signo );
void
main(int argc, char *argv[])
{
        int                     listen_fd = 0;
        char                    netid[] = "x25";
        struct netconfig        *config = NULL;
        struct nd_hostserv      hostserv;
        struct nd_addrlis       *srcaddr = NULL;
        struct t_bind           *bind = NULL;
        struct t_call           *listen = NULL;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
        /*
         *      t_open()
         */
        if(( listen_fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }
        /*
         *      get source address informations
         */
        hostserv.h_host = argv[1];       /* source hostname */
        hostserv.h_serv = argv[2];       /* source servname */
        if( netdir_getbyname( config, &hostserv, &srcaddr ) != 0 ) {
                exit( 3 );
        }
        /*
         *      t_bind()
         */
        if(( bind = (struct t_bind *)t_alloc( listen_fd, T_BIND, T_ADDR )) == NULL ) {
                t_error( "t_alloc of t_bind structure failed" );
                exit( 3 );
        }
        bind->qlen = 1;
        memcpy( bind->addr.buf, srcaddr->n_addrs->buf, srcaddr->n_addrs->len );
        bind->addr.len = srcaddr->n_addrs->len;
        if( t_bind( listen_fd, bind, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 4 );
        }

クライアントの場合と同じく、最初の手順はt_openを呼び出して希望するトランスポート・プロバイダとのトランスポート・エンドポイントを確立することです。このエンドポイントlisten_fdは、接続指示を受付待ちするために使用されます。次にサーバは、ネットワーク・アドレスをこのエンドポイントにバインドします。このアドレスは、各クライアントがサーバにアクセスするために使用します。
t_bindの第2引数は、特定のアドレスをトランスポート・エンドポイントにバインドすることを要求します。この引数は、次に示す形式のt_bind構造体を指します。

struct t_bind {
        struct netbuf   addr;
        unsigned int    qlen;
};

ここで、addrはバインドするアドレスを指定し、qlenはこのエンドポイントに着信する処理可能な最大の接続指示を指定します。X.25-TLIで使用する基本的な構造体および定数は、<tiuser.h>に定義されています。アドレスは、次に示すメンバを含むnetbuf構造体を使用することによって指定されます。

struct netbuf {
        unsigned int    maxlen;
        unsigned int    len;
        char            *buf;
};

ここで、bufはデータを格納しているバッファを指し、lenはバッファ内のデータのバイト数を指定し、maxlenはバッファが保持できるデータの最大バイト数(X.25-TLI関数によって、ユーザにデータが返されるとき以外にはセットする必要はない)を指定します。
t_bind構造体の場合、bufによって指示されるデータは、ネットワーク・アドレスを示しています。ネットワーク・アドレスの構造体については、「8.3.3.2 ネットワーク・アドレス構造体」を参照してください。また、プログラム例で使用しているgetnetconfig(3N),netdir(3N)は、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。

qlenの値が0より大きい場合、トランスポート・エンドポイントは、接続指示の受付待ちをするために使用されます。このような場合、t_bind関数はバインドされたアドレスに向けられた接続指示のキューイングを、ただちに開始することをトランスポート・プロバイダに命令します。さらに、qlenの値は、サーバが処理可能な最大の接続指示の数を指示します。サーバは、コネクションの要求を受け付けるか、拒否するかで、各接続指示に応答しなければなりません。
処理可能な接続指示とは、サーバがまだ応答していない、キューイングされた接続指示のことです。qlenが1の場合、サーバは1つの接続指示を完全に処理してそれに対する応答が終了してから、次の接続指示を受け付けます。しかし、サーバによっては、複数の接続指示を検索した後で、それらに対して応答することを望む場合があります。このような場合、qlenはサーバが処理可能な接続指示の最大数を指定します。

t_alloc(3N)は、t_bindが必要とするt_bind構造体を割り当てるために使用されます。t_allocには3つの引数があります。第1引数は、トランスポート・エンドポイントを参照するファイル記述子です。これは、トランスポート・プロバイダの特性にアクセスするために使用されます(t_open(3N)を参照)。第2引数は、割り当てるべき適切なトランスポート・インタフェース構造体を指定します。第3引数は、第2引数で指定した構造体にnetbufバッファを設定する必要がある場合、どれをこの構造体に割り当てるかを指定します。T_ALLを指定した場合、この構造体に関連付けられているすべてのnetbufバッファを割り当てることを示します。
この例ではT_ALLによってaddrバッファが割り当てられています。このバッファのサイズは、最大アドレス・サイズを定義しているトランスポート・プロバイダの特性から決められます。このnetbuf構造体のmaxlenフィールドには、t_allocによって新たに割り当てられたバッファのサイズが設定されます。
t_allocの詳細は、「8.5 マニュアル・ページ」のt_alloc(3N)を参照してください。

この例のサーバは、1回に1つの接続要求を処理するので、qlenには1がセットされます。さらに、ネーム・トゥ・アドレス・マッピングにより得たアドレス情報はt_allocにより新たに割り当てられたt_bind構造体に対して設定され、そのt_bind構造体は、t_bindの第2引数として使用されています。
X.25-TLIにおいては、復帰時のt_bind構造体(第3引数)に含まれるアドレスは意味がなく、X.25-TLIユーザがt_bindに設定したアドレスがそのまま返されます。

注意:
X.25-TLIにおいては、ネットワーク・インタフェースをトランスポート・インタフェースにマッピングしていますが、アドレスに関しては、ネットワーク・アドレス構造体の形式で扱わなければなりません。ネットワーク・アドレス構造体については、「8.3.3.6 ネットワーク・アドレス構造体」を参照してください。

t_bindが成功すると、プロバイダは接続要求のキューイングを開始し、通信の次のフェーズであるコネクション確立に入ります。

8.3.3.2 ネットワーク・アドレス構造体

ネットワーク・アドレスは、網加入時に付与されるDTEアドレスやネットワークID等を含む、ネットワーク・アドレス構造体の形式をとります。次にネットワーク・アドレス構造体(struct x25addr)を示します。なお、この構造体は、ネーム・トゥ・アドレス・マッピング関数であるnetdir_getbynameルーチンを用いて獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。

<fnetaddr.h>
struct x25addr {
       short       family;                   /* Address family               */
       ushort      resv0;                    /* reserved                     */
       t_scalar_t  nwid;                     /* Network ID                   */
       ushort      addrlen;                  /* Address effective length     */
       ushort      resv1;                    /* reserved                     */
       char        addr[MAXX25ADDR];         /* DTE address                  */
       ushort      subaddrlen;               /* Sub address effective length */
       ushort      resv2;                    /* reserved                     */
       char        subaddr[MAXX25SUBADDR];   /* Sub address field            */
       ushort      proidlen;                 /* Protocol-ID effective length */
       ushort      resv3;                    /* reserved                     */
       char        proidmsk[MAXPROID];       /* Protocol-ID mask data        */
       char        proidcmp[MAXPROID];       /* Protocol-ID compare data     */
};
typedef struct x25addr x25addr_t;

t_bind時においては、ネットワーク・アドレス構造体(struct x25addr)の設定方法によってプロバイダの動作が異なります。次にクライアント/サーバごとに説明します。

◆クライアント

クライアント側の、t_bind時のアドレス情報の設定には、次のような方法があります。

ユーザは、ネーム・トゥ・アドレス・マッピングによってアドレス情報を獲得することができます。詳細は、「8.4.2 ネーム・トゥ・アドレス・マッピング」を参照してください。

注意:
クライアント側のt_connect時には、t_bind時に上記のどの設定方法をとっても、nwid(ネットワークID)、addr(相手DTEアドレス)の設定は必須になります。また、t_bindでネットワークIDを指定した場合、t_connectで指定するネットワークIDも同じ値を設定しなければなりません。
t_bind発行時にアドレス構造体を引数に指定する場合、自ホスト名と自サービス名のペアで、netdir_getbyname によるアドレス構造体の作成を行ってください。また後述のt_connect発行時には、相手ホスト名と相手サービス名のペアで、netdir_getbyname によるアドレス構造体の作成を行います。

◆サーバ

サーバ側での、t_bind時のアドレス情報の設定には、次のような方法があります。

クライアント側と同様に、ユーザはネーム・トゥ・アドレス・マッピングによって、アドレス情報を獲得することができます。詳細は、「8.4.2 ネーム・トゥ・アドレス・マッピング」を参照してください。

注意:
t_bind発行時にアドレス構造体を引数に指定する場合、自ホスト名と自サービス名のペアで、netdir_getbyname によるアドレス構造体の作成を行ってください。

8.3.3.3 コネクション確立

コネクション確立の手順では、クライアントとサーバの動作の相違点がはっきりしています。
X.25-TLIは、このフェーズにおける手順のセットをX.25-TLIユーザに要求しますが、それらの手順は、X.25-TLIユーザのタイプごとに異なります。

クライアントは、t_connect(3N)を使用して特定のサーバに対して接続を要求することによって、コネクション確立の手順を開始します。サーバは、その時にt_listen(3N)を呼び出すことによって、クライアントからの要求が通知されます。サーバは、クライアントの要求を受け付けることも拒否することもでき、t_accept(3N)を呼び出してコネクションを確立するか、あるいはt_snddis(3N)を呼び出して要求を拒否します。クライアントには、t_connect終了時にサーバの決定が通知されます。
X.25-TLIは、コネクションの確立が行われている間、次に示す2つの機能をサポートします。

コネクション確立中にクライアントとサーバの間でデータを転送する機能

クライアントは、コネクション確立を要求する時にデータをサーバに送信することができます。このデータは、t_listenによってサーバに引き渡されます。同様に、サーバは、コネクションを受け付けるか拒否する時にデータをクライアントに送信することができます。
この場合、t_openによって返される接続特性によって、コネクション確立中に双方のユーザ間で転送可能なデータの量を知ることができます。

プロトコル・オプションのネゴシエーション

クライアントは、X.25プロバイダおよび、リモート・ユーザにサポートしてもらいたいプロトコル・オプションを指定できます。X.25-TLIは、ローカルおよびリモートの両方のオプション・ネゴシエーションをサポートします。

これらは、本質的にプロトコル特有の機能です。プロトコル非依存のソフトウェアの開発を目的としている場合には、この機能を使用すると妨げになります。詳細は、「8.3.6 X.25-TLI拡張インタフェース」を参照してください。

◆クライアント

クライアント/サーバの例を続けます。コネクションを確立するためにクライアントが必要とする手順を次に示します。

        /*
         *      get destination address informations
         */
        hostserv.h_host = argv[1];       /* destination hostname */
        hostserv.h_serv = argv[2];       /* destination servname */
        if( netdir_getbyname( config, &hostserv, &destaddr ) != 0 ) {
                netdir_perror("netdir_getbyname() failed");
                exit( 4 );
        }
        /*
         *      t_connect()
         */
        if(( call = (struct t_call *)t_alloc( fd, T_CALL, T_ADDR )) == NULL ) {
                t_error( "t_alloc of t_conenct structure failed" );
                exit( 5 );
        }
        memcpy( call->addr.buf, destaddr->n_addrs->buf, destaddr->n_addrs->len );
        call->addr.len = destaddr->n_addrs->len;
        if((t_connect( fd, call, NULL )) < 0 ) {
                if( t_errno == TLOOK ) && ( t_look( fd ) == T_DISCONNECT )) {
                         fprintf( stderr, "connection rejected \n" );
                }
                t_error( "t_conenct failed" );
                exit( 6 );
        }

t_connectを呼び出すと、サーバとのコネクションが確立されます。t_connectの第1引数は、コネクションが確立されるトランスポート・エンドポイントを識別し、第2引数は、あて先サーバを識別します。この引数は以下の形式のt_call構造体へのポインタです。

struct t_call {
        struct netbuf   addr;           /* address         */
        struct netbuf   opt;            /* options         */
        struct netbuf   udata;          /* user data       */
        int             sequence;       /* sequence number */
};

addrはサーバのアドレスを示し、optはクライアントがコネクションとプロトコル固有のオプションを関連付けることを望んでいる場合に使用し、udataは接続要求とともにユーザ・データをユーザに送信する場合に、そのユーザ・データを指定します。sequenceフィールドは、t_connectに対して意味を持ちません。
t_allocは、t_call構造体を動的に割り当てるために呼び出されています。一度割り当てられると、適切な値が設定されます。この例では、オプションもユーザ・データもt_connect呼出しには関連付けられていませんが、サーバのアドレス情報は設定する必要があります。

X.25-TLIでは、アドレスの形式はネットワーク・アドレス構造体です。設定の際、ネットワーク・アドレス構造体内のDTEアドレス、ネットワークIDの指定は必須となります。例では、netdir(3N)を使用してアドレス構造体を設定しています。詳細は、「8.4.2 ネーム・トゥ・アドレス・マッピング」を参照してください。

t_allocの第3引数には、サーバのアドレスとして適当なnetbufバッファを割り当てることを指定するために、T_ADDRが設定されています。さらにユーザは、サーバのアドレスとしてt_allocで割り当てたnetbuf構造体のbuf,maxlen,lenに、netdir(3N)で獲得したアドレス情報を設定します。
t_connectの第3引数は、新たに確立されたコネクションについての情報をユーザに返すために使用できます。その情報によって、サーバが接続要求への応答の中に含めて送信してきたユーザ・データを検索することができます。この例では、この情報は不要であることを示すために、クライアントはこの引数にNULLをセットしています。

t_connectが成功したとき、コネクションは確立されます。サーバがコネクションの要求を拒否した場合、t_connectは失敗し、t_errnoにTLOOKをセットします。(TLOOKに関する詳細は、「8.3.3.6 事象の処理」を参照してください。)

◆サーバ

例に戻りましょう。クライアントがt_connectを呼び出すと、サーバが受付待ちをしているトランスポート・エンドポイント上で接続指示が発生します。この事象を処理するためにサーバが必要とする手順は、後で説明します。各クライアントに対して、サーバは接続要求を受け付け、そのコネクションを管理するためのサーバ・プロセスを生成します。

        if(( listen = (struct t_call *)t_alloc( listen_fd, T_CALL, T_ALL )) == NULL ) {
                t_error( "t_alloc of t_call structure failed" );
                exit( 5 );
        }
        while( 1 ) {
                if( t_listen( listen_fd, listen ) < 0 ) {
                        t_error("t_listen failed");
                        exit( 6 );
                }
                if(( conn_fd = accept_call( listen_fd, listen )) != -1 ) {
                        run_server( listen_fd );
                }
        }

サーバは永久ループに入り、各接続指示を処理します。最初にサーバは、t_listenを呼び出して、次の接続指示を検索します。接続指示が着信すると、サーバはaccept_callを呼び出してその接続要求を受け付けます。accept_callは、(後で示すように)代替トランスポート・エンドポイント上でコネクションを受け付け、そのエンドポイントの値を返します。
conn_fdは、コネクションが確立されるトランスポート・エンドポイントを識別するグローバル変数です。コネクションが代替エンドポイントで受け付けられるため、サーバは受付待ちのためにバインドされたエンドポイント上で接続指示を待ち続けることができます。この呼出しがエラーなしで受け付けられると、run_serverはこのコネクションを管理するためのプロセスを生成します。

サーバは、t_allocを呼び出してt_listenが使用するためのt_call構造体を割り当てます。t_allocの第3引数であるT_ALLは、呼出し元アドレス、オプション、およびユーザデータを検索するために必要なアドレス情報用のバッファが割り当てられることを指定します。

この例のトランスポート・プロバイダは、コネクション確立中のユーザ・データ転送を使用しておらず、また、プロトコル・オプションも使用していません。したがってt_allocは、ユーザ・データとオプションのためのバッファを割り当てる必要はありません。しかし、呼び出し元アドレスの情報を格納するのに十分なサイズのバッファを割り当てる必要があります。t_allocは、t_openにより返されたaddr値からこのバッファのサイズを決定します。また、各netbuf構造体のmaxlenフィールドには、t_allocによって新たに割り当てられたバッファのサイズがセットされます。

t_call構造体を使用することにより、サーバはt_listenを呼び出して、次の接続指示を検索します。接続指示が現在使用可能であれば、t_listenはただちに正常復帰します。そうでなければ、t_listenは接続指示が着信するまでブロックされます。
接続指示が着信すると、次に示すように、サーバはaccept_callを呼び出してクライアントの要求を受け付けます。

int
accept_call( int listen_fd, struct t_call *listen )
{
        int             res_fd = 0;
        /*
         *      t_open()
         */
        if(( res_fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 7 );
        }
        /*
         *      t_bind()
         */
        if( t_bind( res_fd, NULL, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 8 );
        }
        /*
         *      t_accept()
         */
        if( t_accept( listen_fd, res_fd, listen ) < 0 ) {
                if(( t_errno == TLOOK ) && ( t_look( listen_fd ) == T_DISCONNECT )) {
                        if( t_rcvdis( listen_fd, NULL ) < 0 ) {
                                t_error("t_rcvdis failed");
                                exit( 9 );
                        }
                        if( t_close( res_fd ) < 0 ) {
                                t_error("t_close failed");
                                exit( 10 );
                        }
                        return(-1);
                }
                t_error("t_accept failed (not TLOOK)");
                exit( 11 );
        }
        return res_fd;
}

accept_callは、次の2つの引数をとります。

listen_fd

接続指示が着信するトランスポート・エンドポイントの識別子です。

call

接続指示に関連するすべての情報を含んでいる、t_call構造体へのポインタです。

まず最初に、サーバはトランスポート・プロバイダのcloneデバイス・ノードをオープンし、アドレスをバインドすることによって、もう1つのトランスポート・エンドポイントを確立します。クライアントの場合と同じく、ユーザは、プロバイダによってどのアドレスがバインドされるかを関知しないことを指定するために、t_bindの第2引数にはNULL値が渡されます。新たに確立されたトランスポート・エンドポイントres_fdは、クライアントの接続要求を受け付けるために使用されます。

t_acceptの最初の2つの引数は、受け付け待ち用のエンドポイントと、実際にコネクションの確立動作を行うエンドポイントをそれぞれ指定します。受け付け待ち用エンドポイントで、コネクションの確立動作を行うこともできますが、そうするとその接続の間は他のクライアントがそのサーバにアクセスすることができなくなります。
t_acceptの第3引数は、接続指示に関連するt_call構造体ポインタです。この構造体には、呼出し元ユーザのアドレスおよび、t_listenによって返されたシーケンス番号が入っています。サーバが複数の未処理の接続指示を管理している場合は、sequenceの値は特別な重要性を持ちます。また、t_call構造体にはユーザが指定しようとしているプロトコル・オプションや、クライアントに引き渡されるユーザ・データも指定することができます。
この例のトランスポート・プロバイダは、プロトコル・オプションや、コネクション確立中のユーザ・データ転送を使用していないので、t_listenによって返されるt_call構造体を、t_acceptの第3引数で示されるt_call構造体としてそのまま引き渡しています。

この例を簡単にするために、t_openまたはt_bind呼出しが失敗したときには、サーバは終了します。exit(2)は、listen_fdに関連付けられているトランスポート・エンドポイントをクローズします。さらに、それによってトランスポート・プロバイダは、コネクションを要求したクライアントに切断指示を引き渡します。この切断指示は、コネクションが確立されなかったことを通知します。つまりt_connectが失敗し、t_errnoにはTLOOKがセットされます。

接続指示が受け付けられる前に、受け付け待ち用トランスポート・エンドポイントで非同期事象が発生した場合、t_acceptは失敗し、t_errnoにTLOOKがセットされます。1つだけ未処理の接続指示がある状態で発生するイベントは切断指示だけです。この事象は、クライアントが以前に送信した接続要求を取り消すことを決定した場合に起こります。
切断指示が到着すると、サーバはt_rcvdisを使用して切断指示を受け付けなければなりません。この関数は、t_discon構造体へのポインタを引数としてとり、この構造体は切断指示に関連する情報を検索するために使用されます。しかしこの例では、サーバはこの情報を検索しないので、引数にNULLをセットしています。t_rcvdis(3N)については、「8.3.3.5 コネクション解放」を参照してください。切断指示を受け付けた後、accept_callは応答用のトランスポート・エンドポイントをクローズし、-1を返します。これは、コネクションがクライアントによって解放されたことをサーバに知らせます。そのあとサーバは、次の接続要求を受け付け待ちしています。

図8.11は、サーバがどのようにコネクションを確立するかを示しています。

image

図8.11 受け付け待ち用、応答用トランスポート・エンドポイント

トランスポート・コネクションは、新たに生成された応答用エンドポイント上で確立され、受付待ち用エンドポイントは、次の接続指示の検索のために解放されます。

8.3.3.4 データ転送

一度コネクションが確立されると、クライアントとサーバはt_sndとt_rcvを使用することにより、コネクションを介したデータ転送を開始することができます。X.25-TLIは、この時点以降クライアントとサーバを区別しません。どちらのユーザも、データの送信と受信、およびコネクションの解放を行うことができます。X.25-TLIは、既存のコネクション上での、信頼性のある順序だったデータの配信を保証します。
トランスポート・コネクションを介して転送できるデータは、次の2つのクラスに分類できます。

一般的には、優先データは緊急の情報に関連します。このデータは、X.25のITパケットによって転送されます。データ転送のためのメッセージ・インタフェースは、t_sndとt_rcvの特殊なフラグT_MOREによってサポートされます。

T_MORE

メッセージ境界の概念があるトランスポート・プロトコルを使用する場合、データ転送用のメッセージ・インタフェースは、T_MOREというt_sndとt_rcvのフラグによってサポートされています。2つのトランスポート・ユーザの間では、メッセージはトランスポート・サービス・データ・ユニット(TSDU)と呼ばれるメッセージ単位で転送されます。TSDUの最大サイズは、プロバイダ特性のひとつで、t_openとt_getinfoで知ることができます。ユーザは、最大TSDUサイズの範囲内の複数のメッセージ単位を伝送することができます。トランスポート・コネクションを介して複数のメッセージ単位を送信するには、ユーザは最後の呼出しを除いてt_sndを呼び出すごとにT_MOREフラグをセットする必要があります。このフラグは、ユーザがさらに次のt_sndの呼出しでメッセージに関連するデータを送信することを指定します。最後のメッセージは、それが最後のメッセージ単位であることを指定するためにT_MOREをセットせずに送信されなければなりません。
同様に受信側でも、一連のメッセージを複数のメッセージ単位でユーザに引き渡すことができます。T_MOREフラグがセットされてt_rcvが復帰した場合、ユーザはメッセージの残りを検索するためにt_rcvの呼出しを続ける必要があります。最後のメッセージは、t_rcvを呼び出してT_MOREがセットされなかったときに識別されます。
注意:
T_MOREフラグは、X.25-TLIの下でデータがどのようにパッケージされるか、または、リモート・ユーザへのデータの送信がどのように行われるかについては、何も示唆しません。例えば、ユーザが1回のt_sndの呼出しで1個の完全なメッセージを送信する場合、トランスポート・プロバイダがそのデータを単一のメッセージ単位でリモート・トランスポート・ユーザへ配信するという保証はありません。メッセージ境界の保護は、t_sndやt_rcvのT_MOREフラグの値を注意することによってのみ可能です。これによって、リモートユーザが送信した時のままの内容とメッセージ境界を持つメッセージを受信側ユーザが受け取ることが保証されます。
備考:
X.25のDTパケットで、DビットまたはQビットを制御する機能については、「8.3.7 X.25-TLI固有インタフェース」を参照してください。

◆クライアント

クライアント/サーバの例を続けます。サーバは、トランスポート・コネクションを介してクライアントにログ・ファイルを転送します。クライアントは、このデータを受信して自分の標準出力ファイルに書き込みます。バイト・ストリーム・インタフェースがクライアントとサーバによって使用されているので、メッセージ境界(つまり、T_MOREフラグ)は無視されます。クライアントは、次の命令を使用してデータを受信します。

        while(( nbytes = t_rcv( fd, rcvbuf, RCVDATASIZE, &rcv_flags )) < 0 ) {
                if( fwrite( rcvbuf, 1, nbytes, stdout ) < (size_t)0 ) {
                        fprintf( stderr, "fwrite failed\n" );
                        exit( 7 );
                }
        }

クライアントは、受信データを処理するために、t_rcvを連続して呼び出します。データが存在しない場合、データが受信するまでt_rcvはブロックされます。t_rcvは、クライアントの入力バッファのサイズである1024バイトまでの使用可能データを検索し、受信したバイト数を復帰値として返します。クライアントはこのデータを標準出力に書き込み、さらに処理を続行します。
t_rcvが失敗すると、データ転送フェーズは終了します。本節で後述するように、切断指示が着信した場合、t_rcvは失敗します。

fwrite(3S)の呼出しが失敗するとクライアントは終了し、トランスポート・エンドポイントがクローズされます。データ転送フェーズ中に(exitまたはt_closeによって)トランスポート・エンドポイントがクローズされると、コネクションは強制解放され、リモート・ユーザは切断指示を受信します。

◆サーバ

今度は、コネクションのもう一方の側を見てみましょう。サーバは、クライアントにデータを送信するための子プロセスを生成することにより、自分のデータ転送を管理します。その際、親プロセスはループ・バックをして次の接続指示の受付を待ちます。run_serverは、この子プロセスを生成するために、サーバによって次のように呼び出されます。

void
connrelease( int signo )
{
        if( t_look( conn_fd ) == T_DISCONNECT ) {
                fprintf( stderr, "connection aborted\n" );
                exit( 12 );
        }
        exit( 0 );
}
void
run_server( int listen_fd )
{
        FILE    *logfp = NULL;
        int     nbytes;
        char    buf[1024];
        switch( fork() ) {
        case -1:
                perror("fork failed");
                exit( 20 );
        default:          /* parent */
                if( t_close( conn_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 21 );
                }
                return;
        case 0:           /* child */
                if( t_close( listen_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 22 );
                }
        }
        if(( logfp = fopen( "logfile", "r" )) == NULL ) {
                perror( "cannot open logfile" );
                exit( 23 );
        }
        signal( SIGPOLL, connrelease );
        if( ioctl( conn_fd, I_SETSIG, S_INPUT ) < 0 ) {
                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 ) < 0 ) {
                        t_error( "t_snd failed" );
                        exit( 26 );
                }
        }
        if( t_snddis( conn_fd, NULL ) < 0 ) {
                t_error("t_snddis failed");
                exit( 27 );
        }
        exit( 28 );
}

fork(2)の後、親プロセスは、メイン処理ループに戻って次の接続要求の受付を待ちます。その間、子プロセスは、新たに確立されたトランスポート・コネクションを管理します。fork呼出しが失敗した場合、exitはlisten_fdに関連付けられているトランスポート・エンドポイントをクローズし、切断指示がクライアントに送信され、クライアントのt_connect呼出しは失敗します。

サーバ・プロセスは、fread(3S)により一度にログ・ファイルから1024バイト分のデータを読み込み、それをt_sndを用いてクライアントに送信します。bufはデータ・バッファの先頭を指示し、nbytesは送信するデータのバイト数を指定します。第4引数は、次のオプション・フラグのどれかを含むことができます。

T_EXPEDITED 

優先データであることを指定します。データは、ITパケットによって転送されます。

T_MORE 

コネクションを介して、メッセージを送信する際のメッセージ境界を規定します。

この例では、これらのフラグはどれもセットされません。
ユーザがトランスポート・プロバイダにデータを流しすぎた場合、プロバイダはフロー制御を行います。このような場合、フロー制御が解除されるまでt_sndは停止し、解除後に動作を再開します。t_sndは、nbytesで指定されたバイト数がトランスポート・プロバイダに引き渡されるまでは終了しません。
t_snd関数は、プロバイダにデータを引き渡すまでは、切断指示(コネクションが切断されたことを示す)を検出しません。また、データ・トラフィックは一方向に流れるため、ユーザが着信事象を認識することはできません。そこで、何らかの理由でコネクションが打ち切られた場合、データが失われることがあるので、ユーザに通知する必要があります。

ユーザは、t_sndを呼び出す前にt_lookを起動することで、着信事象の有無を調べることができます。この例では、もっと効率のよい解決法を示しています。それは、ioctlでI_SETSIGを指定することです。これによってユーザは、指定された事象が発生した時にシグナルを要求することができます。(詳細は、streamio(7I)とsignal(3C)を参照してください。)conn_fdによって参照されるストリーム上で入力が到着すると、S_INPUTによってシグナルがユーザに送信されます。切断指示が着信した場合は、シグナル・キャッチング・ルーチン(connrelease)がエラー・メッセージを出力し、処理を終了します。

この例で、もしデータ・トラフィックが双方向に流れたとすると、ユーザはコネクションの切断を監視する必要はなくなります。それは、クライアントがt_sndとt_rcvを交互に呼び出すのであれば、クライアントは、着信する切断指示をt_rcvによって認識することができるからです。

8.3.3.5 コネクション解放

データ転送中の任意の時点で、どちらかのユーザがトランスポート・コネクションを解放して通信を終わらせることができます。
すでに述べたようにX.25-TLIでは、コネクション解放形式については強制終了のみサポートしています。強制終了はコネクションをただちに切断するため、あて先ユーザにまだ到達していないデータが失われてしまうことがあります。
強制終了を行うには、どちらかのユーザがt_snddisを呼び出します。また、トランスポート・プロバイダは、X.25-TLIの下で問題が発生した場合にはコネクションを打ち切ることができます。X.25-TLIでは、ユーザがt_snddisによってコネクションを強制終了する時に、データをリモート・ユーザに送信することができます。

切断指示がリモート・ユーザから通知された場合、ユーザはt_rcvdisを呼び出して切断指示を検索する必要があります。この呼出しは、コネクションが強制終了された理由を識別する理由コードを返し、また(強制終了が、リモート・ユーザによって開始された場合は)接続指示に伴っているユーザ・データがあればそれを返します。この理由コードはプロバイダに固有のコードなので、プロトコル独立のソフトウェアによって解釈される必要があります。

◆サーバ

すべてのデータがサーバによって転送されると、次のようにコネクションが解放されます。

       if( t_snddis( conn_fd, NULL ) < 0 ) {
                t_error("t_snddis failed");
                exit( 27 );
       }

例に示されるように、データ転送を最初に終了させようとするユーザは、t_snddisを使用してコネクションの解放を開始します。この関数は、サーバによってこれ以上のデータが送信されないことをクライアントに知らせます。この例では、データはサーバからクライアントに一方向に転送されるので、サーバはクライアントからデータを受信することはありません。ユーザ・プロセスが、exitを実行せずにトランスポート・エンドポイントをクローズすることを望む場合は、t_closeを呼び出します。

◆クライアント

クライアントから見たコネクションの解放は、サーバから見たコネクションの解放に類似しています。前述したように、クライアントは、t_rcvが失敗するまで着信データの処理を続けます。サーバがt_snddisを使用してコネクションを解放した場合、t_rcvは失敗してt_errnoにTLOOKがセットされます。その際、クライアントは次のようにコネクションの解放を処理します。

       if(( t_errno == TLOOK ) && (t_look( fd ) == T_DISCONNECT )) {
                if( t_rcvdis( fd, NULL ) < 0 ) {
                        t_error( "t_rcvdis failed" );
                        exit( 7 );
                }
        }
        t_error( "t_rcv failed" );

クライアントのトランスポート・エンドポイントで事象が発生すると、クライアントは予期される切断指示が着信したかどうかをチェックします。切断指示が着信している場合、クライアントはt_rcvdisを呼び出して、その指示を処理します。この時点でクライアントは終了し、exitにより自分のトランスポート・エンドポイントをクローズします。

しかし、各ユーザはデータの消失を防止するための処置をとる必要があります。例えば、

というような方法が考えられます。

8.3.3.6 事象の処理

エラー・コードTLOOKは、X.25-TLIにおいては特別な重要性を持ちます。それは、指定されたトランスポート・エンドポイントで、予期しない非同期トランスポート事象が発生したことが、TLOOKエラーとしてユーザへ通知されるからです。このように、TLOOKは未決イベントの発生を通知し、X.25-TLI関数の通常処理が実行されないことを表しています。

X.25-TLIで定義される非同期トランスポート事象を以下に示します。

T_LISTEN

コネクション確立の要求、すなわち接続指示がトランスポート・エンドポイントに着信しました。

T_CONNECT

先に送信した接続要求の確認、すなわち接続確認がトランスポート・エンドポイントに着信しました。この接続確認は、サーバが接続要求を受け付けた時に生成されます。

T_DATA

通常データが、トランスポート・エンドポイントに着信しました。

T_EXDATA

優先ユーザ・データが、トランスポート・エンドポイントに着信しました。優先データについては「データ転送」で説明します。

T_DISCONNECT

コネクションが強制終了されたか、あるいはサーバが接続要求を拒否したという通知、すなわち切断指示がトランスポート・エンドポイントに着信しました。

ユーザは、t_look(3N)関数によってTLOOKエラーが返された場合に、どんな事象が発生したかを判断でき、それに応じて発生した事象を処理することができます。例えば、接続要求が拒否された場合にクライアントに引き渡される事象は、切断指示(T_DISCONNECT)です。

8.3.3.7 プログラム例

◆クライアント

次の例は、「VCモードサービスの概説」の節で説明したVCモードのクライアントのサンプル・プログラムを示しています。このクライアントは、サーバとトランスポート・コネクションを確立し、サーバからデータを受信して、そのデータを標準出力に書き込みます。

コネクションの解放は、t_snddis(3N),t_rcvdis(3N)により強制的に行なわれます。また、その際のデータ消失に対する処理は行っていません。このクライアントは、このマニュアルで示されるVCモード・サーバと通信します。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
#define RCVDATASIZE 1024
extern int      t_errno;
void
main(int argc, char *argv[])
{
        int                    fd = 0;
        char                   netid[] = "x25";
        struct netconfig       *config = NULL;
        struct nd_hostserv     hostserv;
        struct nd_addrlist     *destaddr = NULL;
        struct t_call          *call = NULL;
        int                    nbytes = 0;
        char                   rcvbuf[RCVDATASIZE];
        int                    rcv_flags = 0;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
        /*
         *      t_open()
         */
        if(( fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }
        /*
         *      t_bind()
         */
        if( t_bind( fd, NULL, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 3 );
        }
        /*
         *      get destination address informations
         */
        hostserv.h_host = argv[1];       /* destination hostname */
        hostserv.h_serv = argv[2];       /* destination servname */
        if( netdir_getbyname( config, &hostserv, &destaddr ) != 0 ) {
                exit( 4 );
        }
        /*
         *      t_connect()
         */
        if(( call = (struct t_call *)t_alloc( fd, T_CALL, T_ADDR )) == NULL ) {
                exit( 5 );
        }
        memcpy( call->addr.buf, destaddr->n_addrs->buf, destaddr->n_addrs->len );
        call->addr.len = destaddr->n_addrs->len;
        if((t_connect( fd, call, NULL )) < 0 ) {
                if(( t_errno == TLOOK ) && ( t_look( fd ) == T_DISCONNECT )) {
                           fprintf( stderr, "connection rejected \n" );
                }
                t_error( "t_conenct failed" );
                exit( 6 );
        }
        while(( nbytes = t_rcv( fd, rcvbuf, RCVDATASIZE, &rcv_flags )) > 0 ) {
                if( fwrite( rcvbuf, 1, nbytes, stdout ) < (size_t)0 ) {
                        fprintf( stderr, "fwrite failed\n" );
                        exit( 7 );
                }
        }
        if(( t_errno == TLOOK ) && ( t_look( fd ) == T_DISCONNECT )) {
                if( t_rcvdis( fd, NULL ) < 0 ) {
                        exit( 7 );
                }
        }
        t_error( "t_rcv failed" );
        t_free( (char *)call, T_CALL );
        netdir_free( destaddr, ND_ADDRLIST );
        t_unbind( fd );
        t_close( fd );
        freenetconfigent( config );
        exit( 0 );
}

◆サーバ

次の例は、「8.3.3 VCモード・サービスの概説」の節で説明したVCモードのサーバのサンプル・プログラムを示しています。このサーバは、クライアントとトランスポート・コネクションを確立し、ログファイルをコネクションの相手のクライアントに転送します。以前に示されたVCモード・クライアントのサンプル・プログラムは、このサーバと通信します。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <signal.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
extern int      t_errno;
int             conn_fd;
#define RCVDATASIZE 1024
void  run_server( int listen_fd );
void  connrelease( int signo );
void
main(int argc, char *argv[])
{
        int                    listen_fd = 0;
        char                   netid[] = "x25";
        struct netconfig       *config = NULL;
        struct nd_hostserv     hostserv;
        struct nd_addrlist     *srcaddr = NULL;
        struct t_bind          *bind = NULL;
        struct t_call          *listen = NULL;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
       /*
         *      t_open()
         */
        if(( listen_fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }
        /*
         *      get source address informations
         */
        hostserv.h_host = argv[1];       /* source hostname */
        hostserv.h_serv = argv[2];       /* source servname */
        if( netdir_getbyname( config, &hostserv, &srcaddr ) != 0 ) {
                exit( 3 );
        }
        /*
         *      t_bind()
         */
        if(( bind = (struct t_bind *)t_alloc( listen_fd, T_BIND, T_ADDR )) == NULL ) {
                t_error( "t_alloc of t_bind structure failed" );
                exit( 3 );
        }
        bind->qlen = 1;
        memcpy( bind->addr.buf, srcaddr->n_addrs->buf, srcaddr->n_addrs->len );
        bind->addr.len = srcaddr->n_addrs->len;
        if( t_bind( listen_fd, bind, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 4 );
        }
        if(( listen = (struct t_call *)t_alloc( listen_fd, T_CALL, T_ADDR )) == NULL ){
                t_error( "t_alloc of t_call structure failed" );
                exit( 5 );
        }
        while( 1 ) {
                if( t_listen( listen_fd, listen ) < 0 ) {
                        t_error("t_listen failed");
                        exit( 6 );
                }
                if(( conn_fd = accept_call( listen_fd, listen )) != -1 ) {
                        run_server( listen_fd );
                }
        }
}
int
accept_call( int listen_fd, struct t_call *listen )
{
        int     res_fd = 0;
        /*
         *      t_open()
         */
        if(( res_fd = t_open( "/dev/tpi/nlcovc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 7 );
        }
        /*
         *      t_bind()
         */
        if( t_bind( res_fd, NULL, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 8 );
        }
        /*
         *      t_accept()
         */
        if( t_accept( listen_fd, res_fd, listen ) < 0 ) {
               if(( t_errno == TLOOK ) && ( t_look( listen_fd ) == T_DISCONNECT )) {
                        if( t_rcvdis( listen_fd, NULL ) < 0 ) {
                                t_error("t_rcvdis failed");
                                exit( 9 );
                        }
                        if( t_close( res_fd ) < 0 ) {
                                t_error("t_close failed");
                                exit( 10 );
                        }
                        return(-1);
                }
                t_error("t_accept failed (not TLOOK)");
                exit( 11 );
        }
        return res_fd;
}
void
connrelease( int signo )
{
        if( t_look( conn_fd ) == T_DISCONNECT ) {
                fprintf( stderr, "connection aborted\n" );
                exit( 12 );
        }
        exit( 0 );
}
void
run_server( int listen_fd )
{
        FILE    *logfp = NULL;
        int     nbytes;
        char    buf[1492];
        switch( fork() ) {
        case -1:
                perror("fork failed");
                exit( 20 );
        default:         /* parent */
                if( t_close( conn_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 21 );
                }
                return;
        case 0:           /* child */
                if( t_close( listen_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 22 );
                }
        }
        if(( logfp = fopen( "logfile", "r" )) == NULL ) {
                perror( "cannot open logfile" );
                exit( 23 );
        }
        signal( SIGPOLL, connrelease );
        if( ioctl( conn_fd, I_SETSIG, S_INPUT ) < 0 ) {
                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 ) < 0 ) {
                        t_error( "t_snd failed" );
                        exit( 26 );
                }
        }
        if( t_snddis( conn_fd, NULL ) < 0 ) {
                t_error("t_snddis failed");
                exit( 27 );
        }
        exit( 28 );
}

8.3.4 PVCモード・サービスの概説

ここでは、X.25-TLIのPVCモード・サービスについて述べます。

PVCモード・サービスは相手固定接続であり、ネットワークへの加入時に、通信する相手システムは固定的に決定されます。したがってプロトコル的には、VCモードのようなコネクション確立処理は不要です。しかしX.25-TLIでは、なるべく同一のインタフェースを提供するため、ユーザはコネクション確立処理を行わなければなりません。すべてのユーザは、発呼動作(t_connect)を行うものとします。

なお、X.25 PVCプロトコルには本来接続、切断を意味する電文が存在しません。(PVCとは、相手局とは常時接続状態にある、とするプロトコルであるため)
このため、本ソフトウェアではアプリケーションからの接続・切断要求を相手に通知するための電文としてリセット要求/リセット指示パケット(RQ/RIパケット)を使用しています。
接続と切断を意味する電文が同一の電文であるため、回線異常が発生した場合など回線切断を意味するRQ/RIパケットを、接続を意味するRQ/RIパケットと取り間違える恐れがあります。この時の状態ずれ(切断のRQ/RIパケットを接続のRQ/RIパケットと、その後の接続のRQ/RIパケットを切断のRQ/RIパケットと取り違えてしまうことにより、接続状態と切断状態が2台のマシン間で食い違い、2度と通信できなくなる状態)を抑止するため、接続のためであると認識したRQ/RIパケットを受信後、データの送受信がない限りRQ/RIパケットを受信してもアプリケーションに切断を通知しないようになっています。
つまり、t_connect()→(一切データ通信せず)→相手局側でt_snddis()というシーケンスになった場合、相手局のt_snddis()によるRQ/RIパケットに対応してアプリケーションに切断を通知することはありません。

PVCは常時相手局と接続しているような形態で使用されるプロトコルであり、相手局と接続・切断を繰り返す場合はVCプロトコルを使用するよう、強く推奨します。
PVCプロトコルにおいて、どの電文を接続・切断の意味で使用するかについての一般的な規約は存在しません。そのため、他機種と接続する場合は事前にどの電文を接続・切断の意味で使用するかについて調査が必要です。
本ソフトウェアとPVCで接続するためには、本ソフトウェアと同様に、接続・切断の電文としてRQ/RIパケットを使用していなければなりません。それ以外の電文を接続・切断に使用するシステムと通信することは不可能です。

8.3.4.1 ローカル管理

PVCモード・サービスにおいてコネクションを確立するには、まずt_openにより、特殊ファイル/dev/tpi/nlcopvcをオープンします。次にt_bindの用意として、t_allocによりt_bind構造体を獲得します。そして、buf内のアドレス構造体に自DTEアドレスとネットワークIDを設定し、t_bindによりプロバイダに通知します。この自DTEアドレスの設定は、コネクションを確立する双方とも必須です。

注意:
自DTEアドレスを含むネットワーク・アドレス情報の獲得は、VCモードと同様にネットワーク・セレクションgetnetconfig(3N)とネーム・トゥ・アドレス・マッピングnetdir(3N)を使用して獲得します。詳細は、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。また、ネットワーク・アドレス構造体については、「8.3.4.2 ネットワーク・アドレス構造体」を参照してください。

次にPVCモード・サービスでは、必ずt_optmgmt(3N)により、使用するPVCの論理チャネル番号をプロバイダに通知しなければなりません。t_optmgmt(3N)については、「8.3.6.2 X.25プロトコル上の制限を受ける関数」のt_optmgmtを参照してください。ローカル管理における一連のプログラム例は、「プログラム例」を参照してください。

8.3.4.2 ネットワーク・アドレス構造体

PVCモード・サービスのネットワーク・アドレスもVCモード・サービスと同様に、加入時に付与されるDTEアドレスや、ネットワークIDなどを含むネットワーク・アドレス構造体の形式をとります。ネットワーク・アドレス構造体の形式については、VCモード・サービスの「8.3.3.2 ネットワーク・アドレス構造体」を参照してください。

PVCモード・サービスでは、t_bindにおいて、アドレス構造体(struct x25addr)内のDTEアドレス領域に自DTEアドレスを、ネットワークID領域にネットワークIDを設定します。これらの指定は必須です。また、プロトコルIDとサブアドレスは使用しないため、これらのフィールドは無効です。
コネクション確立処理(t_connect)を行う時には、アドレス構造体内のDTEアドレス領域の相手DTEアドレスと、ネットワークID領域にt_bind時に指定したものと同じネットワークIDを設定します。これらの設定は必須です。

8.3.4.3 コネクション確立

PVCモード・サービスでは、通信する双方からのt_connectにより、コネクションが確立されます。まずt_allocにより、t_call構造体を獲得します。ネットワーク・アドレス(addr)に相手DTEアドレス、ネットワークIDを設定します。これらの指定は必須です。なお、このネットワーク・アドレスは、ネットワーク・セレクションと、ネーム・トゥ・アドレス・マッピングを用いて獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。

t_connectにて、該当するラインのコネクション動作を行います。この操作により、プロバイダはデータ通信が可能となります。

注意:
PVCモード・サービスでは、通信を行う双方の発呼動作によりコネクション確立を行うため、VCモードサービスでサポートしている関数のうち、着信動作を行うt_listen,t_accept関数はサポートしていません。

8.3.4.4 データ転送

PVCモード・サービスとVCモード・サービスとは、コネクション確立するための動作は異なりますが、データ転送に関しては同様です。「8.3.3.4 データ転送」を参照してください。

8.3.4.5 コネクション解放

PVCモード・サービスのコネクション解放は、概説に記載した点を除けばVCモード・サービスと同様です。「8.3.3.5 コネクション解放」を参照してください。

8.3.4.6 プログラム例

◆PVCモード・トランザクション

次の例は、「8.3.4 PVCモード・サービスの概説」で説明したPVCモード・トランザクションのサンプル・プログラムを示しています。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <signal.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
#include <fnetif.h>
extern intt_errno;
#define RCVDATASIZE 1024
void
main(int argc, char *argv[])
{
        int                     fd = 0;
        char                    netid[] = "x25pvc";
        struct netconfig        *config = NULL;
        struct nd_hostserv      hostserv;
        struct nd_addrlist      *srcaddr = NULL, *destaddr = NULL;
        struct t_bind           *bind = NULL;
        struct t_optmgmt        *req = NULL, *ret = NULL;
        struct t_call           *call = NULL;
        struct par_options      par;
        int                     nbytes = 0;
        char                    rcvbuf[RCVDATASIZE];
        int                     rcv_flags = 0;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
        /*
         *      t_open()
         */
        if(( fd = t_open( "/dev/tpi/nlcopvc", O_RDWR, NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }

最初に、t_openによりPVCモード・サービスの特殊ファイル/dev/tpi/nlcopvcをオープンします。

        /*
         *      get source address informations
         */
        hostserv.h_host = argv[1];       /* source hostname */
        hostserv.h_serv = argv[2];       /* source servname */
        if( netdir_getbyname( config, &hostserv, &srcaddr ) != 0 ) {
                exit( 3 );
        }
        /*
         *      t_bind()
         */
        if(( bind = (struct t_bind *)t_alloc( fd, T_BIND, T_ADDR )) == NULL ) {
                t_error( "t_alloc of t_bind structure failed" );
                exit( 4 );
        }
        bind->qlen = 0;
        memcpy( bind->addr.buf, srcaddr->n_addrs->buf, srcaddr->n_addrs->len );
        bind->addr.len = srcaddr->n_addrs->len;
        if( t_bind( fd, bind, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 5 );
        }

次に、netdir_getbyname(3N)によりネットワーク・アドレスを獲得します。さらに、t_allocによりt_bind構造体を獲得して値を設定し、t_bindにより自局アドレスを設定します。

        /*
         *      t_optmgmt
         */
        par.optname = argv[5];
        par.netbuf = NULL;
        if( netdir_options( config, ND_GET_PARM, fd, (char *)&par ) != 0 ) {
                netdir_perror( "cannot get option informations" );
                exit( 6 );
        }
        if(( req = (struct t_optmgmt *)t_alloc( fd, T_OPTMGMT, 0 )) == NULL ) {
                t_error("t_alloc of t_optmgmt(req) structure failed" );
                exit( 7 );
        }
        if(( ret = (struct t_optmgmt *)t_alloc( fd, T_OPTMGMT, T_OPT )) == NULL ) {
                t_error("t_alloc of t_optmgmt(ret) structure failed" );
                exit( 8 );
        }
        req->opt = *(par.netbuf);
        req->flags = T_NEGOTIATE;
        if( t_optmgmt( fd, req, ret ) < 0 ) {
                t_error("t_optmgmt failed" );
                exit( 9 );
        }

次に、netdir_options(3N)によりオプション情報を獲得します。さらに、通知用のt_optmgmt構造体の領域と、復帰値用のt_optmgmt構造体の領域を獲得し、通知用の領域の方にオプション情報を設定してt_optmgmtを発行します。これで、使用するPVCの論理チャネル番号がプロバイダに通知されます。

        /*
         *      get destination address informations
         */
        hostserv.h_host = argv[3];       /* destination hostname */
        hostserv.h_serv = argv[4];       /* destination servname */
        if( netdir_getbyname( config, &hostserv, &destaddr ) != 0 ) {
                netdir_perror("netdir_getbyname() failed");
                exit( 10 );
        }
        /*
         *      t_connect()
         */
        if(( call = (struct t_call *)t_alloc( fd, T_CALL, T_ADDR )) == NULL ) {
                t_error( "t_alloc of t_connect structure failed" );
                exit( 11 );
        }
        memcpy( call->addr.buf, destaddr->n_addrs->buf, destaddr->n_addrs->len );
        call->addr.len = destaddr->n_addrs->len;
        if((t_connect( fd, call, NULL )) < 0 ) {
                t_error( "t_connect failed" );
                exit( 12 );
        }

次に、netdir_getbyname(3N)により相手のネットワーク・アドレスを獲得します。さらに、t_allocによりt_call構造体を獲得して値を設定し、t_connectを発行します。t_connectの正常終了時にコネクションは確立されます。PVC接続の場合は、必ず双方にてこのサンプルの処理動作が行わなければなりません。コネクションが確立された後のデータ転送動作は、VCモード・サービスと同様です。

8.3.5 より高度な機能

ここでは、非ブロック(非同期)モードでのライブラリ・コールを使用した、イベント駆動型サーバ・アプリケーションのサンプル・プログラムを示します。

8.3.5.1 プログラム例

/*
 *      TLI (for X.25) test program
 *               server
 */
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stropts.h>
#include <signal.h>
#include <poll.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
extern int      t_errno;
int             conn_fd;
#define RCVDATASIZE 1024
void  run_server( int listen_fd );
void  connrelease( int signo );
void
main(int argc, char *argv[])
{
        int                   listen_fd = 0;
        char                  netid[] = "x25";
        struct netconfig      *config = NULL;
        struct nd_hostserv    hostserv;
        struct nd_addrlist    *srcaddr = NULL;
        struct t_bind         *bind = NULL;
        struct t_call         *listen = NULL;
        /*
         *      get config entry
         */
        if(( config = getnetconfigent( netid )) == NULL ) {
                printf( "getnetconfigent( %s ) failed\n", netid );
                exit( 1 );
        }
        /*
         *      t_open()
         */
        if(( listen_fd = t_open( "/dev/tpi/nlcovc", ( O_RDWR | O_NONBLOCK ), NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 2 );
        }
        /*
         *      get source address informations
         */
        hostserv.h_host = argv[1];       /* source hostname */
        hostserv.h_serv = argv[2];       /* source servname */
        if( netdir_getbyname( config, &hostserv, &srcaddr ) != 0 ) {
                exit( 3 );
        }
        /*
         *      t_bind()
         */
        if(( bind = (struct t_bind *)t_alloc( listen_fd, T_BIND, T_ALL )) == NULL ) {
                t_error( "t_alloc of t_bind structure failed" );
                exit( 3 );
        }
        bind->qlen = 1;
        memcpy( bind->addr.buf, srcaddr->n_addrs->buf, srcaddr->n_addrs->len );
        bind->addr.len = srcaddr->n_addrs->len;
        if( t_bind( listen_fd, bind, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 4 );
        }
        while( 1 ) {
                int      event = 0;
                if(( event = poll_event( listen_fd )) < 0 ) {
                        perror( "poll fail" );
                        exit( 5 );
                }
                if( event == T_LISTEN ) {
                        /*
                         *      t_listen()
                         */
                        if(( listen = (struct t_call *)t_alloc( listen_fd, T_CALL, T_ADDR ))
                                                                            == NULL ) {
                                t_error( "t_alloc of t_call structure failed" );
                                exit( 6 );
                        }
                        if( t_listen( listen_fd, listen ) < 0 ) {
                                t_error("t_listen failed");
                                exit( 7 );
                        }
                        if(( conn_fd = accept_call( listen_fd, listen )) != -1 ) {
                                run_server( listen_fd );
                        }
                } else {
                        exit( 8 );
                }
                t_free( (char *)listen, T_CALL );
                continue;
        }
}
int
accept_call( int listen_fd, struct t_call *listen )
{
        int     res_fd = 0;
        /*
         *      t_open()
         */
        if(( res_fd = t_open( "/dev/tpi/nlcovc", ( O_RDWR | O_NONBLOCK ), NULL )) < 0 ) {
                t_error( "t_open failed" );
                exit( 7 );
        }
        /*
         *      t_bind()
         */
        if( t_bind( res_fd, NULL, NULL ) < 0 ) {
                t_error( "t_bind failed" );
                exit( 8 );
        }
        /*
         *      t_accept()
         */
        if( t_accept( listen_fd, res_fd, listen ) < 0 ) {
                if(( t_errno == TLOOK ) && ( t_look( listen_fd ) == T_DISCONNECT )) {
                        if( t_rcvdis( listen_fd, NULL ) < 0 ) {
                                t_error("t_rcvdis failed");
                                exit( 9 );
                        }
                        if( t_close( res_fd ) < 0 ) {
                                t_error("t_close failed");
                                exit( 10 );
                        }
                        return(-1);
                }
                t_error("t_accept failed (not TLOOK)");
                exit( 11 );
        }
        return res_fd;
}
void
connrelease( int signo )
{
        if( t_look( conn_fd ) == T_DISCONNECT ) {
                fprintf( stderr, "connection aborted\n" );
                exit( 12 );
        }
        exit( 0 );
}
void
run_server( int listen_fd )
{
        FILE    *logfp = NULL;
        int     nbytes;
        char    buf[1492];
        switch( fork() ) {
        case -1:
                perror("fork failed");
                exit( 20 );
        default:        /* parent */
                if( t_close( conn_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 21 );
                }
                return;
        case 0:          /* child */
                if( t_close( listen_fd ) < 0 ) {
                        t_error("t_close failed");
                        exit( 22 );
                }
        }
          if(( logfp = fopen( "logfile", "r" )) == NULL ) {
                perror( "cannot open logfile" );
                exit( 23 );
        }
        signal( SIGPOLL, connrelease );
        if( ioctl( conn_fd, I_SETSIG, S_INPUT ) < 0 ) {
                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 ) < 0 ) {
                        t_error( "t_snd failed" );
                        exit( 26 );
                }
        }
        if( t_snddis( conn_fd, NULL ) < 0 ) {
                t_error("t_snddis failed");
                exit( 27 );
        }
        exit( 28 );
}
int
poll_event( int fd )
{
        int             event = 0;
        struct pollfd   pollfds;
        pollfds.events = (POLLIN | POLLPRI);
        pollfds.fd = fd;
        pollfds.revents = 0;
        if( poll( &pollfds, 1, INFTIM ) < 0 ) {
                perror( "poll fail" );
                exit( 31 );
        }
        if( pollfds.revents & (POLLIN | POLLPRI)) {
                event = t_look( pollfds.fd );
                return(event);
        } else {
                return(-1);
        }
}

8.3.6 X.25-TLI拡張インタフェース

ここでは、X.25-TLIを使用する上での注意事項と、X.25プロトコル上の制限を受ける関数、およびその内容を示します。

8.3.6.1 X.25-TLI関数を使用する上での注意事項

8.3.6.2 X.25プロトコル上の制限を受ける関数

◆t_accept関数

t_call構造体のoptには、ユーザ・アプリケーションが要求するオプション情報を設定できます。この時のオプション・バッファのフォーマットを図8.12に示します。protparm構造体については、<fnetparm.h>に定義されています。

image

図8.12 オプション・バッファのフォーマット

注意:
ユーザがオプション情報を使用する場合は、必ずnetdir_options(3N)を使用してください。ただし、netdir_optionsで獲得したオプション・バッファのフォーマットは、t_optmgmt関数で記述しているようなfopthdr構造体が付加された形式となるため、本関数で使用する場合にはoptlen,*bufを操作し、上記オプション・バッファのフォーマットとなるように設定してください。

t_call構造体のudataには、コネクション確立を要求した相手トランスポート・ユーザに返すユーザ・データが設定できます。本関数は、PVCモード・サービスにおいてはサポートされません。

◆t_bind関数

t_bind構造体のaddrは、ネットワーク・アドレスを指定します。X.25-TLIでは、ネットワーク・アドレスはネットワーク・アドレス構造体(struct x25addr)の形式をとります。アドレス構造体の設定方法は、クライアント、サーバにそれぞれ3つあります。それぞれの方法によりプロバイダの動作や通知アドレス値(*ret)が異なります。ネットワーク・アドレス構造体については、各サービス・モードの「ネットワーク・アドレス構造体」を参照してください。

◆t_connect関数

t_call構造体のaddrは、ネットワーク・アドレスを指定します。X.25-TLIでは、ネットワーク・アドレスはネットワーク・アドレス構造体(struct x25addr)の形式をとります。sndcallにおいて、ネットワーク・アドレス構造体内の相手DTEアドレス、ネットワークIDの指定は必須です。ただし、指定するネットワークIDは、必ずt_bind時に設定した値と同一のものを設定します。
t_bindが呼び出された時に、自DTEアドレスもネットワークIDも指定されずに特定ラインにbindされていない場合、プロバイダは本関数で指定されたネットワークIDと同じネットワークIDをもつラインを1つ選択し、コネクションの確立処理が行われます。
t_bind時にアドレス構造体を設定し、特定ラインにbindされている場合、本関数で指定する相手ネットワークIDは、t_bind時に指定したものと同じネットワークIDでなければなりません。
t_call構造体のoptには、アプリケーションが要求するオプション情報を設定することができます。

注意:
オプション情報については、t_accept関数と同様に、必ずnetdir_options(3N)を使用してください。オプション・バッファのフォーマットも、t_accept関数と同じです。

t_call構造体のudataには、接続の実行中に相手トランスポート・ユーザに渡すユーザ・データが設定できます。

◆t_getinfo関数

t_info構造体には、以下の値が設定されます。

addr

ネットワーク・アドレス情報(ネットワーク・アドレス構造体)のバイト数。

options

オプション情報設定エリアのバイト数。

tsdu

トランスポート・コネクションを通じて送信されるメッセージの最大サイズ。X.25-TLIでは、コネクションの確立前は、デフォルト値の4096バイトが設定され、コネクション確立後は、ネゴシエーション後の送信パケットサイズが設定されます。

etsdu

トランスポート・コネクションを通して送信される優先メッセージの最大バイト数。

connect

コネクション確立中に引き渡されるユーザ・データの最大バイト数。

discon

コネクションの打切り解放中に引き渡されるユーザ・データの最大バイト数。

servtype

    T_COTS  現在のサービスは、コネクション型サービスです。

◆t_listen関数

t_call構造体のoptには、着信の際に相手側のトランスポート・ユーザにより要求されたオプション情報が設定されます。オプション・バッファのフォーマットは、t_accept関数と同じです。オプション情報は、ユーザ・アプリケーションにとって有効なデータはありません。
本関数は、PVCモード・サービスにおいては、サポートされません。

◆t_optmgmt関数

ラインごとに定義したデフォルト・パス制御情報(/etc/opt/FJSVwan/etc/x25/parameters)と異なる設定値で通信を行いたいアプリケーションは、本関数を使用してX.25プロバイダに通知しなければなりません。なお通知するオプション情報は、変更するパラメタのみです。
PVCモードでコネクションを確立する場合、必ず本関数にてPVCモードの定義情報をプロバイダに通知しなければなりません。

オプション情報については、t_acceptと同様に、必ずnetdir_options(3N)を使用してください。

netdir_optionsを使用して獲得したオプション・バッファのフォーマットを、図8.13に示します。fopthdr構造体については、<fnetif.h>に定義されています。

image

図8.13 netdir_optionsで得られるオプション・バッファのフォーマット

◆t_rcv関数

flags内にT_MORE,T_EXPEDITEDフラグが設定されます。X.25のDTパケットで、DビットまたはQビットを制御する機能については、「8.3.7.1 D/Qビット制御機能」を参照してください。

◆t_rcvconnect関数

t_call構造体のoptには、着信時に相手側のトランスポート・ユーザにより要求されたオプション情報が設定されます。オプション・バッファのフォーマットは、t_accept関数と同じです。オプション情報は、ユーザ・アプリケーションにとって有効なデータはありません。

◆t_snd関数

flags内にT_MORE,T_EXPEDITEDフラグを設定できます。X.25のDTパケットで、DビットまたはQビットを制御する機能については、「8.3.7.1 D/Qビット制御機能」を参照してください。
この関数は、送信要求したデータがプロバイダに受け付けられた時点で復帰します。したがって、この関数の復帰と、回線上へのパケット送出とは同期していません。(これは、Dビットの付加を要求した場合も同じです。)
送信要求されたデータの大きさが、t_info構造体のTSDUで示す値(ネゴシエーション後の送信パケットサイズ)よりも大きかった場合、TSDUの大きさでMビット分割された複数のパケットとして送信されます。

8.3.7 X.25-TLI固有インタフェース

X.25-TLIでは、次のX.25プロトコル固有制御インタフェースを追加しています。ここでは、次の2つの機能について説明します。

8.3.7.1 D/Qビット制御機能

X.25-TLIでは、次のストリームioctl独自関数により、D/QビットがオンされたDTパケットを送受信する機能がサポートされています。

TLNL_SET_RFLAG

DビットがオンされたDTパケットを送信する場合、またはQビットがオンされたDTパケットを送信する場合に、t_sndの呼出しに先立って、本独自ioctlを呼出します。
呼び出す際には、D/Qフラグ設定領域(nlsetrflag構造体)をstrioctl構造体に設定し、dqflagに付加したいビットの情報を指定します。それぞれの構造体のフォーマットを以下に示します。

<stropts.h>
/*
 *      User level ioctl format for ioctls that go downstream (I_STR)
 */
struct strioctl {
        int      ic_cmd;         /* command         */
        int      ic_timout;      /* timeout value   */
        int      ic_len;         /* length of data  */
        char     *ic_dp;         /* pointer to data */
};
<fnetif.h>
struct nlsetrflag {
        int      dqflag;          /* D/Q flag:T_DON/T_QON   */
};

以下にdqflagに指定できる情報を示します。この値は、すべてビット・イメージになっています。

T_DON

DビットがオンされたDTパケットを送信する場合。

T_QON

QビットがオンされたDTパケットを送信する場合。

ただし、本独自ioctlに続けて呼び出すt_sndにおいて、flagsにT_EXPEDITEDフラグを指定した場合には、上記フラグは意味を持ちません。指定した情報は、flagsにT_EXPEDITEDフラグを指定していない次のt_snd呼出しまで(コネクションが切断されない限り)保持されます。

TLNL_GET_RFLAG

受信したDTパケットに、DビットまたはQビットがオンされているかどうかを知りたい場合に、t_rcvの呼出しに続けて本独自ioctlを呼出します。
呼び出す際には、D/Qフラグ設定領域(nlgetrflag構造体)をstrioctl構造体に設定します。nlgetrflag構造体のフォーマットを以下に示します。

<fnetif.h>
struct nlgetrflag {
        int      dqflag;          /* D/Q flag:T_DON/T_QON   */
};

以下にdqflagに設定される情報を示します。この値は、すべてビット・イメージになっています。

T_DON

DビットがオンされたDTパケットを受信した場合。

T_QON

QビットがオンされたDTパケットを受信した場合。

さらに、ioctl(TLNL_SET_RFLAG)とioctl(TLNL_GET_RFLAG)のサンプルを、それぞれ示します。

/*
 *      ioctl(TLNL_SET_RFLAG)
 */
#include <stropts.h>
#include <fnetif.h>
              ・
              ・
        int                     fd;
        struct strioctl         sti;
        struct nlsetrflag       sflag;
        int                     sndbytes = 256;
        char                    sndbuf[256];
              ・
              ・
        /*
         *      Connection was established, and send DATA packet (D bit ON)
         */
        sti.ic_cmd = TLNL_SET_RFLAG;
        sti.ic_timout = 0;
        sti.ic_len = sizeof(struct nlsetrflag);
        sflag.dqflag = T_DON;           /* set T_DON */
        sti.ic_dp = (char *)&sflag;
        if( ( ioctl( fd, I_STR, (char *)&sti )) < 0 ) {
                perror( "ioctl(TLNL_SET_RFLAG) error\n" );
                exit( 1 );
        }
        if( t_snd( fd, sndbuf, sndbytes, 0 ) < 0 ) {
                t_error( "t_snd failed" );
                exit( 2 );
        }

ioctlの第1引数のfdは、t_open()で得たファイル識別子です。
このioctlは、Dビットを付加したいデータをt_snd()によって送信要求する直前に呼び出します。

/*
 *      ioctl(TLNL_GET_RFLAG)
 */
#include <stropts.h>
#include <fnetif.h>
#define RCVDATASIZE 256
              ・
              ・
        int                     fd;
        struct strioctl         sti;
        struct nlgetrflag       rflag = 0;
        int                     rcvbytes = 0;
        char                    rcvbuf[256];
        int                     rcv_flags = 0;
              ・
              ・
        /*
         *      Connection was established, and receive DATA packet check (D bit ON)
         */
        if(( rcvbytes = t_rcv( fd, rcvbuf, RCVDATASIZE, &rcv_flags )) < 0 ) {
                t_error( "t_rcv failed" );
                exit( 1 );
        }
        sti.ic_cmd = TLNL_GET_RFLAG;
        sti.ic_timout = 0;
        sti.ic_len = sizeof(struct nlgetrflag);
        sti.ic_dp = (char *)&rflag;
        if( ( ioctl( fd, I_STR, (char *)&sti )) < 0 ) {
                perror( "ioctl(TLNL_SET_RFLAG) error\n" );
                exit( 1 );
        }
        printf( "D/Q flag = 0x%x\n", rflag.dqflag )

ioctlの第1引数のfdは、t_open()で得たファイル識別子です。
このioctlは、t_rcv()によって受信したデータにD/Qビットが付加されているかどうかを知りたい場合、t_rcv()の直後に呼び出します。復帰時には、D/Qビットの付加情報が設定されています。

どちらのioctlも、正常終了時には0が、異常終了時には-1が返ります。
なお、ioctlおよびエラー・コードの詳細については、streamio(7I),termio(7I)を参照してください。

8.3.7.2 disconnect情報

次のX.25-TLIのストリームioctl独自関数により、以下の機能がサポートされています。

X25_SCAUSE

自側から切断する場合のdisconnect情報(CAUSE値、診断符号)を設定します。

X25_GCAUSE

相手から切断された場合のdisconnect情報(CAUSE値、診断符号)を読み出します。

以上の独自ioctlを呼び出す場合には、必ずdisconnect情報設定領域(disdata構造体)をstrioctl構造体に設定しなければなりません。それぞれの構造体のフォーマットを示します。

<stropts.h>
/*
 *      User level ioctl format for ioctls that go downstream (I_STR)
 */
struct strioctl {
        int      ic_cmd;         /* command         */
        int      ic_timout;      /* timeout value   */
        int      ic_len;         /* length of data  */
        char     *ic_dp;         /* pointer to data */
};
<fnetif.h>
struct disdata {
        t_scalar_t    DISCON_reason;    /* disconnect reason code */
        t_scalar_t    DIAG_code;        /* disconnect code        */
};

さらに、ioctl(X25_SCAUSE)とioctl(X25_GCAUSE)のサンプルを、それぞれ示します。

/*
 *   ioctl(X25_GCAUSE)
 */
#include <stropts.h>
#include <fnetif.h>
            ・
            ・
        int                    fd;
        struct strioctl        sti;
        struct disdata         data;
            ・
            ・
        /*
         *      Connection was disconnected
         */
            ・
            ・
        sti.ic_cmd = X25_GCAUSE;
        sti.ic_timout = 0;
        sti.ic_len = sizeof(struct disdata);
        sti.ic_dp = (char *)&data;
        if( (ioctl( fd, I_STR, (char *)&sti ) ) < 0 ) {
                perror("ioctl(X25_GCAUSE)\n");
                exit( 1 );
        }
        printf("DISCON_reason = 0x%x, DIAG_code = 0x%x\n",
                data.DISCON_reason, data.DIAG_code );

ioctlの第1引数のfdは、t_open()で得たファイル識別子です。
このioctlは、相手から非同期に切断された場合に呼び出します。例えば、「8.3.3.7 プログラム例」のクライアント側のような一方向の通信中に、受信側でt_rcvが異常終了(TLOOKが通知された)し、t_look()の結果T_DISCONNECTであった場合などです。復帰時には、切断時のdisconnect情報(CAUSE値、診断符号)が設定されています。

/*
 *      ioctl(X25_SCAUSE)
 */
#include <stropts.h>
#include <fnetif.h>
            ・
            ・
        int                     fd;
        struct strioctl         sti;
        struct disdata          data;
            ・
            ・
        sti.ic_cmd = X25_SCAUSE;
        sti.ic_timout = 0;
        sti.ic_len = sizeof(struct disdata);
        data.DISCON_reason = CAUSE;      /* set CAUSE */
        data.DIAG_code = DIAG;           /* set DIAG */
        sti.ic_dp = (char *)&data;
        if( (ioctl( fd, I_STR, (char *)&sti ) ) < 0 ) {
                perror("ioctl(X25_SCAUSE)\n");
                exit( 1 );
        }
        /*
         *      t_snddis()
         */
            ・
            ・

このioctlは、自側から切断する直前に呼び出します。例えば、「8.3.3.7 プログラム例」のサーバ側のような一方向の通信において、送信が完了し意識的にCAUSE,DIAGを相手に通知したい場合、t_snddisを発行する直前などです。

どちらのioctlも、正常終了時には0が、異常終了時には-1が返ります。
なお、ioctlおよびエラー・コードの詳細については、streamio(7I),termio(7I)を参照してください。


目次 前ページ次ページ

All Rights Reserved, Copyright (C) 富士通株式会社 2002