Netcompo WAN制御 説明書 |
目次
![]() ![]() |
ここでは、HDLC手順を使用し、アプリケーションが他システムと通信を行う場合のインタフェースについて説明します。
HDLC手順使用時のアプリケーション・インタフェースは、TLIのコネクション・モードにマッピングされており、アプリケーションはTLIを使用する場合とほぼ同じ方法でネットワークを使用し、通信することができます。
ただし、HDLC手順独自の機能も追加されており、以降本インタフェースをHDLC-TLIと記述し、TCP/IP手順でのTLIと区別します。
コネクション・モードHDLC-TLIは、アプリケーションに対して、TLIと同様のインタフェースを提供することを目的としています。
このため、データリンク・プロバイダ・インタフェースをトランスポート・プロバイダ・インタフェースにマッピングしています。
図8.2にHDLC-TLIの概要を示します。
図8.2 HDLC-TLIの概要
HDLCインタフェース・プロバイダは、上位にデータリンク・レイヤ・インタフェースを提供します。
これをTLI変換モジュールがトランスポート・レイヤ・インタフェースに変換することにより、ユーザに対してHDLC-TLIインタフェースを提供します。
したがって、データリンク層のサービスはトランスポート層のサービスにマッピングされ、データリンク・エンドポイント(データリンク・コネクションの端点)はトランスポート・エンドポイントにマッピングされています。
以降の記述では、基本的にデータリンク層がトランスポート層にマッピングされていることを前提に、トランスポートのインタフェース用語で記述します。
図8.3にマッピングのモデルを、また、表8.1に対応する用語を示します。
図8.3 マッピングモデル
表8.1 トランスポート層用語に対応するデータリンク層用語一覧
本文中の記述 |
マッピング対象 |
意味 |
トランスポート・エンドポイント |
データリンク・エンドポイント |
1つのトランスポート・エンドポイントは、1つのデータリンク・エンドポイントに対応しています。 |
トランスポート・コネクション |
データリンク・コネクション |
HDLCで独立の通信を行える最小単位であり、NRMまたはABMに対応します。 |
トランスポート・プロバイダ |
コンバート・モジュール+HDLCプロバイダ |
HDLCプロバイダは、データリンク・インタフェースを提供しますが、このインタフェースは、コンバート・モジュールによりトランスポート・インタフェースに変換されます。したがって、コンバート・モジュール+HDLCプロバイダが、1つのトランスポート・プロバイダと見なすことができます。 |
HDLC-TLIには、以下の2つのモードがあります。
ABMはHDLC-ABM手順を使用するものであり、NRMはHDLC-NRM手順を使用するものです。
ABMのサービスは、以下の4つのフェーズによって特徴づけられます。
ローカル管理フェーズは、HDLC-TLIユーザとトランスポート・プロバイダ間のローカル動作を定義します。前述のとおり、トランスポート・プロバイダは、HDLCプロバイダとコンバート・モジュールとみなします。例えばユーザは、図8.4で示されるように、トランスポート・プロバイダとの通信のチャネルを確立する必要があります。HDLC-TLIユーザとトランスポート・プロバイダの間の各チャネルは、通信の一意のエンドポイントであり、トランスポート・エンドポイントと呼ばれます。
t_open(3N)によって、ユーザはABMサービスを行なう特定のトランスポート・プロバイダを選択することができ、さらにそのトランスポート・エンドポイントを確立することができます。
図8.4 ユーザとプロバイダ間のチャネル
各ユーザにとって必要なもう1つのローカル機能は、トランスポート・プロバイダの実体を確立することです。各ユーザはデータリンク・アドレスによって識別されます。より正確に言えば、データリンク・アドレスは各トランスポート・エンドポイントと関連しており、1つのユーザ・プロセスがいくつかのトランスポート・エンドポイントを管理できます。
ABMサービスでは、あるユーザは別のユーザのアドレスを指定することによって、そのユーザとのコネクションを要求します。データリンク・アドレスの構造はデータリンク・プロバイダのアドレス形式によって定義され、各トランスポート・エンドポイントに対するアドレスの割当ては、t_bind(3N)を使用して行います。
t_openやt_bindの他にも多くの関数がローカル動作をサポートするために使用できます。表8.2にHDLC-TLIインタフェースのすべてのローカル管理関数を要約します。
表8.2 HDLC-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.5に示されるように、各ユーザが相互の間にコネクションを生成することができます。
図8.5 トランスポート・コネクション
このフェーズは、各HDLC-TLIユーザ間のクライアント/サーバ関係によって表すことができます。通常、一方のユーザがサーバとなり、ユーザ・グループに対してある種のサービスを宣言して、これらのユーザからの要求を待ちます。各クライアントはサービスを必要とするときに、サーバが通知したデータリンク・アドレスを使用して自分とサーバを接続しようとします。
関数t_connect(3N)は、接続要求を開始します。t_connectの引数の1つであるデータリンク・アドレスは、クライアントがアクセスしたいサーバを指示します。サーバは、t_listen(3N)によってクライアントからの接続要求が送られてきたことを知り、t_accept(3N)を呼び出してサービスへのアクセスを求めるクライアントの要求を受け付けます。要求が受け付けられると、トランスポート・コネクション(データリンク・コネクション)が確立されます。
表8.3にHDLC-TLIのコネクションを確立するために使用できる、すべての関数を要約します。
表8.3 HDLC-TLIのコネクションを確立するための関数
関数 |
説明 |
t_accept |
トランスポート・コネクションの要求を受け付ける。 |
t_connect |
指定されたあて先のHDLC-TLIユーザとのコネクションを確立する。 |
t_listen |
他のHDLC-TLIユーザからの接続要求を検索、受信する。 |
t_rcvconnect |
t_connectが非同期モードで呼び出されていた場合には、コネクションの確立を完了する。 |
データ転送フェーズでは、ユーザは確立されたコネクション上での双方向のデータ転送を行うことができます。
t_snd(3N)とt_rcv(3N)の2つの関数は、このコネクション上でのデータの送信と受信を行います。ユーザが送信するすべてのデータは、送信した順番通りにこのコネクションの他端のユーザに送信されます。
表8.4にHDLC-TLIのデータ転送を行なうために使用できる、すべての関数を要約します。
表8.4 HDLC-TLIのデータ転送を行うための関数
関数 |
説明 |
t_rcv |
トランスポート・コネクションを通して到着したデータを受信する。 |
t_snd |
確立されたトランスポート・コネクションを通してデータ送信する。 |
コネクション解放フェーズでは、ユーザは確立されたコネクションを切断することができます。ユーザは、通信を終了させることを決定した時に、プロバイダがトランスポート・コネクションを解放するように要求することができます。
t_snddis(3N)はこの強制切断を開始し、t_rcvdis(3N)は強制切断のための着信指示を処理します。
この強制切断により、既に送信され、他方のトランスポート・ユーザにまだ到着していないデータは、すべてトランスポート・プロバイダによって廃棄されます。
表8.5にコネクション解放を行なうために使用できるすべての関数を要約します。
表8.5 HDLC-TLIのコネクションを解放するための関数
関数 |
説明 |
t_rcvdis |
理由コードやユーザ・データを含めて、打ち切られたコネクションの指示を返す。 |
t_snddis |
コネクションを打ち切るか、または接続要求を拒否する。 |
NRMのサービスは、プロトコル上親子関係があり、P局(Primaly Station)側が主体となって動作することが決められているため、S局(Secondary Station)側からのコネクション確立等はできません。このため、HDLC-TLIユーザからの発着信を指定することはできません。
ユーザは、NRMサービスで通信を行う双方において、必ずコネクト動作(t_connect)を行わなければなりません。したがってNRMサービスでは、ABMサービスがサポートする関数のうち、次の関数はサポートしません。
HDLC-TLIは次の2つの構成要素を持ちます。
状態遷移規則は、状態情報および事象の処理に基づいています。これらの事象には、ユーザが発生させたライブラリ呼出しだけでなく、プロバイダが発生させた事象指示も含まれます。
注意:
HDLC-TLIのユーザは、このインタフェースを用いてソフトウェアを記述する前に、起こりうるすべての状態遷移について理解しておく必要があります。
ここでは、HDLC-TLIのABMサービスについて述べます。前節で説明したように、ABMサービスはクライアント/サーバのモデルを用いて例示することができます。2つのプログラミング例を用いて、ABMサービスの重要な概念を提示します。これらの例は関連しており、1つの例はクライアントがどのようにサーバへのコネクションを確立し、そのサーバと通信するかを例示し、もう1つの例は対となるサーバ側を示します。ここで示す例は、「プログラム例」でその全体が示されます。
これらの例では、クライアントは、サーバ・プロセスとのコネクションを確立します。サーバは、その時にクライアントにファイルを転送します。次にクライアントは、サーバからのデータを受信して、そのデータをクライアントの標準出力ファイルに書き込みます。
クライアントとサーバがトランスポート・コネクションを確立するための条件として、両者は各自で、まずt_openを使用してトランスポート・プロバイダへのローカル・チャネル(トランスポート・エンドポイント)を確立し、次にt_bindを使用して自分の実体(トランスポート・エンドポイントを他のトランスポート・エンドポイントと識別するために割り当てられるアドレス)を確立する必要があります。
HDLC-TLIユーザは、t_openにより1つのトランスポート・エンドポイントを設定します。実際は、トランスポート・エンドポイントはデータリンク・エンドポイントにマッピングされているため、HDLCプロバイダにデータリンク・エンドポイントを設定することになります。
t_openは、このデータリンク・エンドポイントに関する以下の情報を、<tiuser.h>で定義されているt_info構造体の形式でユーザに返します。
addr
データリンク・アドレス情報(データリンク・アドレス構造体)の最大サイズ。
options
トランスポート・ユーザとトランスポート・プロバイダの間で引き渡されるプロトコル固有オプションの最大バイト数。
tsdu
トランスポート・コネクションを通じて送信されるメッセージの最大サイズ。HDLC-TLIでは、bindされてラインが特定するまでは、デフォルト値の16384バイトが設定され、ライン特定後は、そのラインのconfig情報として定義されている、最大送信フレーム長 - 2バイトが設定されます。
etsdu
トランスポート・コネクションを通じて送信される優先データ・メッセージの最大サイズ。HDLC-TLIでは、未サポート。
connect
コネクション確立中にユーザ間で引き渡されるユーザ・データの最大バイト数。HDLC-TLIでは、未サポート。
discon
コネクション解放中にユーザ間で引き渡されるユーザ・データの最大バイト数。HDLC-TLIでは、未サポート。
servtype
トランスポート・プロバイダによりサポートされるサービスのタイプ。HDLC-TLIでは、以下のサービス・タイプとなります。
T_COTS 現在のサービスは、コネクション型サービスです。
注意:
t_openは、トランスポート・エンドポイントに関連付けられているデフォルトのプロバイダ特性を返します。しかし、特性の一部はエンドポイントがオープンされた後で変更されることがあります。t_getinfoを呼び出すことにより、トランスポート・エンドポイントの現在の特性が検索できます。
ユーザは、選択したトランスポート・プロバイダとのトランスポート・エンドポイントを確立した後、自分の実体を確立しなければなりません。前述のように、t_bindはデータリンク・アドレスをトランスポート・エンドポイントにバインドすることによってこれを達成します。さらにサーバ側では、この関数はトランスポート・プロバイダに対して、その用途が接続指示の受付のために使用されるものであるか、あるいは接続要求の呼出しに使用されるものであるかを指示することができます。
オプションの関数t_optmgmt(3N)も、ローカル管理フェーズ中に使用できます。ユーザは、この関数によってプロトコル・オプションの値をトランスポート・プロバイダとネゴシエートすることができます。プロトコルごとに、ネゴシエート可能なプロトコル・オプション群が独自に規定されています。このオプションはプロトコル固有の性質のため、この機能を使用できるのは特定のプロトコル環境用に作成されたアプリケーションだけです。
この例のクライアントとサーバのローカル管理の要件を使用して、これらの機能を詳細に説明します。次に示すのは、クライアントのプログラムが必要とする定義と、必要なローカル管理手順です。
#include <stdio.h> #include <fcntl.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[] = "hdlcabm"; 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/hdlcabm", 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引数は、ABMサービスを提供するHDLCプロトコルを識別するファイル・システム・ノードのパス名です。この例で示す/dev/tpi/hdlcabmは、総称的なコネクション型HDLC-ABMプロトコルを識別する特殊ファイル(デバイス)名です。このデバイスは、第2引数であるO_RDWRオープン・フラグの指定に応じて、読み出しおよび、書き込み用にオープンされます。
第3引数は、トランスポート・プロバイダのサービス特性をユーザに返すために使用されます。プロトコルに依存しないソフトウェアを書く時に役に立ちます。ここに示す例のクライアントとサーバはこの情報を無視し、トランスポート・プロバイダが以下の特性を持っていると仮定しています。
これらの特性は、ユーザによっては必要とされないので、t_openへの第3引数にはNULLが指定されています。
t_openの戻り値は、以後すべてのHDLC-TLIインタフェース関数の呼出しによって使用される、トランスポート・エンドポイントに対する識別子です。この識別子は、実際にはトランスポート・プロトコル・ファイルをオープンすることにより得られるファイル記述子です。(open(2)を参照してください。)トランスポート・エンドポイントが生成された後、クライアントはエンドポイントにアドレスを割り当てるために、t_bindを呼び出します。第1引数は、トランスポート・エンドポイントを指定します。第2引数は、ユーザがエンドポイントに割り当てようとしているアドレスを指定します。第3引数は、HDLC-TLIにおいては意味がありません。
サーバのトランスポート・エンドポイントに関連付けられたアドレスは、すべてのクライアントがサーバにアクセスするために使用するアドレスであるため重要です。しかし、一般のクライアントは他のプロセスからアクセスされることはないので、自分のアドレスを気にする必要はありません。この例も同様であるため、t_bindの第2引数にはNULLが設定されています。第2引数にNULLが設定されると、トランスポート・プロバイダは適当なラインを選択します。
注意:
クライアントにおいて、t_bindの第2引数にアドレスを指定する場合、qlenには必ず0を設定してください。
t_openかt_bindのどちらかが失敗すると、プログラムはt_error(3N)を呼び出して適切なエラー・メッセージを標準エラー出力に出します。HDLC-TLI関数のどれかが失敗すると、グローバル整数t_errnoに該当するトランスポート・エラー値が設定されます。HDLC-TLIにおけるエラー値は、<tiuser.h>で定義されており、t_errorはt_errnoの値に応じたエラー・メッセージを出力します。この関数は、errnoの値に基づいたエラー・メッセージを出力するperror(3C)に類似しています。トランスポート機能に関連したエラーがシステム・エラーである場合、t_errnoにはTSYSERRが設定され、errnoには<hdlc_if.h>で定義されたプロトコル固有のエラー値が設定されます。
この例のサーバは、通信を開始する前に、同様なローカル管理の手順を行う必要があります。サーバは、接続指示を受付待ちをするためのトランスポート・エンドポイントを確立しなければなりません。必要な定義およびローカル管理手順を次に示します。
#include <stdio.h> #include <fcntl.h> #include <tiuser.h> #include <netconfig.h> #include <netdir.h>
extern int t_errno; int listen_fd = 0; void run_server( int fd );
#define READBUFSIZE 1492
void main( int argc, char *argv[] ) {
char netid[] = "hdlcabm"; struct netconfig *config = NULL; int listen_fd = 0;
/* * get config entry */
if(( config = getnetconfigent( netid )) == NULL ) { printf( "getnetconfigent( %s ) failed\n", netid ); exit( 1 ); }
while( 1 ) { int res_fd = 0; struct nd_hostserv hostserv; struct nd_addrlist *srcaddr = NULL; struct t_bind *bind = NULL; struct t_call *listen = NULL;
/* * t_open() */
if(( listen_fd = t_open( "/dev/tpi/hdlcabm", 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 ) { netdir_perror("netdir_getbyname() failed"); 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; bind->addr.maxlen = srcaddr->n_addrs->maxlen; bind->addr.len = srcaddr->n_addrs->len; bind->addr.buf = srcaddr->n_addrs->buf;
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はこのエンドポイントに着信する処理可能な最大の接続要求の数を指定します。しかし、ABMサービスでは1つのエンドポイントにおいて複数の着信指示を待つことはできないため、qlenには必ず1を設定します。HDLC-TLIで使用する基本的な構造体および定数は、<tiuser.h>に定義されています。
アドレスは、次に示すメンバを含むnetbuf構造体を使用することによって指定されます。
struct netbuf { unsigned int maxlen; unsigned int len; char *buf; };
ここで、bufはデータを格納しているバッファを指し、lenはバッファ内のデータのバイト数を指定し、maxlenはバッファが保持できるデータの最大バイト数(HDLC-TLI関数によって、ユーザにデータが返されるとき以外にはセットする必要はない)を指定します。
t_bind構造体の場合、bufによって指示されるデータは、データリンク・アドレスを示しています。データリンク・アドレスの構造体については、「8.2.3.2 データリンク・アドレス構造体」を参照してください。また、プログラム例で使用しているgetnetconfig(3N),netdir(3N)については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。
qlenの値が0より大きい場合、トランスポート・エンドポイントは、接続指示の受付待ちをするために使用されます。このような場合、t_bind関数はバインドされたアドレスに向けられた接続指示のキューイングを、ただちに開始することをトランスポート・プロバイダに命令します。さらにqlenの値は、サーバが処理可能な最大の接続指示の数を指定します。
注意:
ABMサービスでは、1つのエンドポイントにおいて複数の接続指示を待つことはできないため、未処理の接続指示は存在しません。したがって、接続指示を要求する場合には、必ずqlenに1を設定してください。
サーバは、コネクションの要求を受け付けるかまたは、拒否するかで、各接続指示に応答しなければなりません。
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)を参照してください。
この例のサーバでは、ネーム・トゥ・アドレス・マッピングにより得たアドレス情報は、t_allocにより新たに割り当てられたt_bind構造体に対して設定され、そのt_bind構造体は、t_bindの第2引数として使用されています。HDLC-TLIにおいては、復帰時のt_bind構造体(第3引数)に含まれるアドレスは意味がなく、HDLC-TLIユーザがt_bindに設定したアドレスがそのまま返されます。
注意:
HDLC-TLIおいては、データリンク・インタフェースをトランスポート・インタフェースにマッピングしていますが、アドレスに関しては、データリンク・アドレス構造体の形式で扱わなければなりません。データリンク・アドレス構造体については、「8.2.3.2 データリンク・アドレス構造体」を参照してください。
t_bindが成功すると、プロバイダは接続要求のキューイングを開始し、通信の次のフェーズであるコネクション確立に入ります。
データリンク・アドレスは、局アドレスや交換網加入時に付与されるダイアル番号等を含むデータリンク・アドレス構造体の形式をとります。次にデータリンク・アドレス構造体(struct hdlcaddr)を示します。なお、この構造体は、ネーム・トゥ・アドレス・マッピング関数であるnetdir_getbynameルーチンを用いて獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。
<fnetaddr.h>
struct hdlcaddr { struct hdlcaddr_s addr; /* essential addrress */ struct hdlcaddr_o optaddr; /* essential addrress */ ushort rxidlen; /* receive XID effect length */ ushort sxidlen; /* send XID effect length */ char rxid[MAXXID]; /* receive XID */ char sxid[MAXXID]; /* send XID */ ushort optprmlen; /* effect option para length */ ushort resv7; /* reserved */ char hdlcopt[MAXOPTLEN]; /* option parameter */ }; typedef struct hdlcaddr hdlcaddr_t;
次にデータリンク・アドレス構造体内にあるメイン・アドレス領域(struct hdlcaddr_s)とオプション・アドレス領域(struct hdlcaddr_o)の構造体形式を示します。
struct hdlcaddr_s { ushort family; /* address family */ ushort linemode; /* linemode */ t_scalar_t nwid; /* Network ID */ char addrflag; /* address flag */ char addr; /* terminal address */ char sabmlen; /* SABM effect length */ char sabm; /* SABM mode */ ushort diallen; /* Dial effect length */ ushort resv2; /* reserved */ char dialno[MAXDIALNO]; /* Dial number */ ushort subaddrlen; /* Sub address length for ISDN */ ushort resv3; /* reserved */ char subaddr[MAXHDLCSUBADDR]; /* Sub address for ISDN */ char subaddrflag1; /* Sub address kind flag1 for ISDN */ char subaddrkind1; /* Sub address kind1 for ISDN */ char subaddrflag2; /* Sub address kind flag2 for ISDN */ char subaddrkind2; /* Sub address kind2 for ISDN */ char afiflag; /* AFI flag for ISDN */ char aficode; /* AFI code for ISDN */ ushort resv4; /* reserved */ unsigned char hdlcrsv[4]; };
struct hdlcaddr_o { t_scalar_t optnwid; /* option Network-ID */ char optaflag; /* address flag */ char optaddr; /* option terminal address */ ushort resv5; /* reserved */ ushort optdiallen; /* option dial effect length */ ushort resv6; /* reserved */ char optdialno[MAXDIALNO]; /* option dial no */ ushort optsubadlen; /* option sub address length for ISDN*/ ushort resv7; /* reserved */ char optsubaddr[MAXHDLCSUBADDR];/* option sub address for ISDN */ char optsubadflag1; /* option sub address kind flag1 for ISDN*/ char optsubadkind1; /* option sub address kind1 for ISDN */ char optsubadflag2; /* option sub address kind flag2 for ISDN*/ char optsubadkind2; /* option sub address kind2 for ISDN */ char optafiflag; /* option AFI flag for ISDN */ char optaficode; /* option AFI code for ISDN */ ushort resv8; /* reserved */ unsigned char hdlcrsv[4]; };
t_bind時においては、アドレス構造体(struct hdlcaddr)の設定方法によって、プロバイダの動作が異なります。次に、クライアント/サーバごとに説明します。
クライアント側の、t_bind時のアドレス情報の設定には、次のような方法があります。
ユーザは、ネーム・トゥ・アドレス・マッピングによってアドレス情報を獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。
注意:
クライアント側のt_connect時には、t_bind時に上記のどの設定方法をとっても、アドレス構造体(接続する相手のアドレス情報)の指定は必須です。また、t_bindでネットワークIDを指定した場合、t_connectで指定するネットワークIDも同じ値を設定しなければなりません。詳細は、「8.2.3.3 コネクション確立」を参照してください。
t_bind発行時にアドレス構造体を引数に指定する場合、自ホスト名と自サービス名(接続相手ホストが設定されたもの)のペアで、netdir_getbyname によるアドレス構造体の作成を行ってください。また後述のt_connect発行時には、相手ホスト名と相手サービス名(自ホストが設定されたもの)のペアで、netdir_getbyname によるアドレス構造体の作成を行います。
サーバ側でも、t_bind時のアドレス情報の設定には、次のことに注意しなければなりません。
クライアント側と同様に、ユーザはネーム・トゥ・アドレス・マッピングによって、アドレス情報を獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。
注意:
t_bind発行時にアドレス構造体を引数に指定する場合、自ホスト名と自サービス名(接続相手ホストが設定されたもの)のペアで、netdir_getbyname によるアドレス構造体の作成を行ってください。
コネクション確立の手順では、クライアントとサーバの動作の相違点がはっきりしています。
HDLC-TLIは、このフェーズにおける手順のセットをHDLC-TLIユーザに要求しますが、それらの手順はHDLC-TLIユーザのタイプごとに異なります。
クライアントは、t_connect(3N)を使用して特定のサーバに対して接続を要求することによって、コネクション確立の手順を開始します。サーバは、その時にt_listen(3N)を呼び出すことによって、クライアントからの要求が通知されます。サーバは、クライアントの要求を受け付けることも拒否することもでき、t_accept(3N)を呼び出してコネクションを確立するか、あるいは、t_snddis(3N)を呼び出して要求を拒否します。クライアントには、t_connect終了時にサーバの決定が通知されます。
HDLC-TLIは、コネクションの確立が行われている間、次に示す機能をサポートします。
クライアントは、HDLCプロバイダおよびリモート・ユーザにサポートしてもらいたいプロトコル・オプションを指定できます。HDLC-TLIは、ローカルおよびリモートの両方のオプション・ネゴシエーションをサポートします。
このオプション・ネゴシエーションは、本質的にプロバイダ特有の機能です。プロトコル非依存のソフトウェアの開発を目的としている場合には、この機能を使用すると妨げになります。詳細は、「8.2.6 HDLC-TLI拡張インタフェース」を参照してください。
注意:
HDLC-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 ); }
call->addr.maxlen = destaddr->n_addrs->maxlen; call->addr.len = destaddr->n_addrs->len; call->addr.buf = destaddr->n_addrs->buf; call->opt.maxlen = 0; call->opt.len = 0; call->opt.buf = NULL;
if((t_connect( fd, call, NULL )) < 0 ) { t_error( "t_connect 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はHDLC-TLIではサポートしていません。sequenceフィールドはt_connectに対して意味を持ちません。
t_allocは、t_call構造体を動的に割り当てるために呼び出されています。一度割り当てられると、適切な値が設定されます。この例では、t_call構造体のaddrにサーバのアドレス情報をセットする必要があります。HDLC-TLIでは、アドレスの形式はデータリンク・アドレス構造体です。例では、netdir(3N)を使用してアドレス構造体を設定しています。詳細は、「8.4.2 ネーム・トゥ・アドレス・マッピング」を参照してください。
t_allocの第3引数には、サーバのアドレスとして適当なnetbufバッファを割り当てることを指定するために、T_ADDRが設定されています。さらにユーザは、サーバのアドレスとしてt_allocで割り当てたnetbuf構造体のbuf,maxlen,lenに、netdir(3N)で獲得したアドレス情報を設定します。
t_connectが成功したとき、コネクションは確立されます。サーバがコネクションの要求を拒否した場合、t_connectは失敗してt_errnoにTLOOKをセットします。(TLOOKに関する詳細は、「8.2.3.6 事象の処理」を参照してください。)
例に戻りましょう。クライアントがt_connectを呼び出すと、サーバが受付待ちしているトランスポート・エンドポイント上で接続指示が発生します。そして、次にコネクションの確立処理が行われます。
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 ); }
if( t_listen( listen_fd, listen ) < 0 ) { t_error("t_listen failed"); exit( 6 ); }
res_fd = listen_fd; if( t_accept( listen_fd, res_fd, listen ) < 0 ) { if(t_errno == TLOOK) { if(( t_look( listen_fd )) == T_DISCONNECT ) { if( t_rcvdis( listen_fd, NULL ) < 0 ) { t_error("t_rcvdis failed"); break; } } } }
run_server( listen_fd );
注意:
ABMサービスでは、サーバは、1つのエンドポイントにおいて複数クライアントからの接続指示を受け付けることができないため、t_acceptによるエンドポイントの切替えは許されていません。t_acceptを呼ぶ際には、新たにトランスポート・エンドポイントをオープンせずに、上記の例のようにt_listenを行ったlisten_fdと同じエンドポイント(res_fd)を指定し、そのエンドポイントでコネクションの確立を行います。もし、ユーザが違うエンドポイントを誤って指定した場合には、t_acceptはエラー終了します。
一度コネクションが確立されると、クライアントとサーバはt_sndとt_rcvを使用することにより、コネクションを介したデータ転送を開始することができます。HDLC-TLIは、この時点以降クライアントとサーバを区別しません。どちらのユーザも、データの送信と受信、およびコネクションの解放を行うことができます。HDLC-TLIは、既存のコネクション上での、信頼性のある順序だったデータの配信を保証します。
注意:
HDLC-TLIでは、HDLCプロトコル上、t_snd,t_rcvの引数flagsは何ら意味を持ちません。また、優先データはサポートしていません。
クライアント/サーバの例を続けます。サーバは、トランスポート・コネクションを介してクライアントにログ・ファイルを転送します。クライアントはこのデータを受信して、自分の標準出力ファイルに書き込みます。クライアントは次の命令を使用してデータを受信します。
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によって)トランスポート・エンドポイントがクローズされると、コネクションは強制解放され、リモート・ユーザは切断指示を受信します。
今度は、コネクションのもう一方の側を見てみましょう。
void conn_release( int signo ) {
if( t_look( listen_fd ) != T_DISCONNECT ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); } exit( 0 ); }
void run_server( int fd ) {
FILE *logfp = NULL; int n = 0; char buf[1024];
if((logfp = fopen( "logfile", "r")) == NULL ) { perror("cannot open logfile"); exit( 20 ); }
signal( SIGPOLL, conn_release ); if( ioctl( fd, I_SETSIG, S_INPUT ) < 0 ) { perror("cannot open logfile"); exit( 21 ); }
if( t_look( fd ) != 0 ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); }
while(( n = fread( buf, 1, 1024, logfp )) < 0 ) { if( t_snd( fd, buf, n, 0 ) < 0 ) { t_error( "t_snd failed" ); exit( 23 ); } }
if( t_snddis( fd, NULL ) < 0 ) { t_error("t_snddis failed" ); exit( 24 ); } return; }
サーバ・プロセスは、fread(3S)により、一度にログ・ファイルから1024バイト分のデータを読み込み、それをt_sndを用いてクライアントに送信します。bufはデータ・バッファの先頭を指示し、nbytesは送信するデータのバイト数を指定します。第4引数は、HDLC-TLIでは何ら意味を持ちません。
ユーザがトランスポート・プロバイダにデータを流しすぎた場合、プロバイダはフロー制御を行います。このような場合、フロー制御が解除されるまでt_sndは停止し、解除後に動作を再開します。t_sndは、nbytesで指定されたバイト数がトランスポート・プロバイダに引き渡されるまでは終了しません。
t_snd関数は、プロバイダにデータを引き渡すまでは、切断指示(コネクションが切断されたことを示す)を検出しません。また、データ・トラフィックは一方向に流れるため、ユーザが着信事象を認識することはできません。そこで、何らかの理由でコネクションが打ち切られた場合、データが失われることがあるので、ユーザに通知する必要があります。
ユーザはt_sndを呼び出す前に、t_lookを起動することで着信事象の有無を調べることができます。この例では、もっと効率のよい解決法を示しています。それは、ioctlでI_SETSIGを指定することです。これによってユーザは、指定された事象が発生した時にシグナルを要求することができます。(詳細は、streamio(7I)とsignal(3C)を参照してください。)conn_fdによって参照されるストリーム上で入力が到着すると、S_INPUTによってシグナルがユーザに送信されます。切断指示が着信した場合は、シグナル・キャッチング・ルーチン(conn_release)がエラー・メッセージを出力し、処理を終了します。
この例で、もしデータ・トラフィックが双方向に流れたとすると、ユーザはコネクションの切断を監視する必要はなくなります。それは、クライアントがt_sndとt_rcvを交互に呼び出すのであれば、クライアントは着信する切断指示をt_rcvによって認識することができるからです。
データ転送中の任意の時点で、どちらかのユーザがトランスポート・コネクションを解放して通信を終了することができます。
すでに述べたようにHDLC-TLIでは、コネクション解放形式については強制終了のみサポートしています。強制終了はコネクションをただちに切断するため、あて先ユーザにまだ到達していないデータが失われてしまうことがあります。
強制終了を行うには、どちらかのユーザがt_snddisを呼び出します。また、トランスポート・プロバイダは、HDLC-TLIの下で問題が発生した場合にはコネクションを打ち切ることができます。
注意:
HDLC-TLIでは、ユーザがt_snddisによってコネクションを強制終了する時に、データをリモート・ユーザに送信することはできません。
コネクションの打切りがリモート・ユーザから通知された場合、t_rcvdisを呼び出して切断指示を検索する必要があります。この呼出しは、コネクションが強制終了された理由を識別する理由コードを返します。この理由コードはプロバイダに固有のコードなので、プロトコル独立のソフトウェアによって解釈される必要があります。
注意:
HDLC-TLIでは、t_rcvdis(3N)がエラー・コードTOUTSTATEで異常終了することがあります。詳細は、「8.2.6.2 HDLCプロトコルの制限を受ける関数」のt_rcvdis(3N)関数を参照してください。
すべてのデータがサーバによって転送されると、次のようにコネクションが解放されます。
if( t_snddis( fd, NULL ) < 0 ) { t_error("t_snddis failed" ); exit( 24 ); }
例に示されるように、データ転送を終了させようとするユーザは、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( 8 ); } }
t_free( (char *)call, T_CALL ); netdir_free( destaddr, ND_ADDRLIST ); t_close( fd ); freenetconfigent( config ); exit( 0 );
クライアントのトランスポート・エンドポイントで事象が発生すると、クライアントは、予期される解放指示が着信したかどうかをチェックします。解放指示が着信している場合、クライアントはt_rcvdisを呼び出して、その指示を処理します。この時点でクライアントは終了し、exitにより自分のトランスポート・エンドポイントをクローズします。
しかし、各ユーザはデータの消失を防止するための処置をとる必要があります。例えば、
エラー・コードTLOOKエラーは、HDLC-TLIにおいては特別な重要性を持ちます。それは、指定されたトランスポート・エンドポイントで、予期しない非同期トランスポート事象が発生したことが、TLOOKエラーとしてユーザへ通知されるからです。このように、TLOOKは未決イベントの発生を通知し、HDLC-TLI関数の通常処理が実行されないことを表しています。HDLC-TLIで定義される非同期トランスポート事象を以下に示します。
T_LISTEN
コネクション確立の要求、すなわち接続指示がトランスポート・エンドポイントに着信しました。
T_CONNECT
先に送信した接続要求の確認、すなわち接続確認がトランスポート・エンドポイントに着信しました。この接続確認は、サーバが接続要求を受け付けた時に生成されます。
T_DATA
通常データがトランスポート・エンドポイントに着信しました。
T_DISCONNECT
コネクションが強制終了されたか、あるいは相手側が接続要求を拒否したという通知、すなわち切断指示がトランスポート・エンドポイントに着信しました。
ユーザは、t_look(3N)関数によってTLOOKエラーが返された場合に、どんな事象が発生したかを判断でき、それに応じて発生した事象を処理することができます。例えば、接続要求が拒否された場合にクライアントに引き渡される事象は、切断指示(T_DISCONNECT)です。
次の例は、「8.2.3 ABMサービスの概説」で説明したABMサービスのクライアントのサンプル・プログラムを示しています。このクライアントは、サーバとトランスポート・コネクションを確立し、サーバからデータを受信して、そのデータを標準出力に書き込みます。コネクションの解放は、t_snddis(3N),t_rcvdis(3N)により強制的に行われます。また、その際のデータ消失に対する処理は行っていません。このクライアントは、このマニュアルで示されるABMサービスのサーバと通信します。
#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[] = "hdlcabm"; struct netconfig *config = NULL; struct nd_hostserv hostserv; struct nd_addrlist *destaddr = NULL; struct t_call *call = NULL; int nbytes = 0; char rcvbuf[1024]; 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/hdlcabm", 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 ) { 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 ) { t_error( "t_connect 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 ) { t_error( "t_rcvdis failed" ); exit( 8 ); } }
t_free( (char *)call, T_CALL ); netdir_free( destaddr, ND_ADDRLIST ); t_close( fd ); freenetconfigent( config ); exit( 0 ); }
次の例は、「8.2.3 ABMサービスの概説」で説明したABMサービスのサーバのサンプル・プログラムを示しています。このサーバは、クライアントとトランスポート・コネクションを確立し、ログ・ファイルをコネクションの相手のクライアントに転送します。以前に示されたABMサービスのクライアントのサンプル・プログラムは、このサーバと通信します。
#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 listen_fd = 0;
void run_server( int fd );
#define READBUFSIZE 1492
void main(int argc, char *argv[]) {
char netid[] = "hdlcabm"; struct netconfig *config = NULL;
/* * get config entry */
if(( config = getnetconfigent( netid )) == NULL ) { printf( "getnetconfigent( %s ) failed\n", netid ); exit( 1 ); }
while( 1 ) { int res_fd = 0; struct nd_hostserv hostserv; struct nd_addrlist *srcaddr = NULL; struct t_bind *bind = NULL; struct t_call *listen = NULL;
/* * t_open() */ if(( listen_fd = t_open( "/dev/tpi/hdlcabm", 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 ) { netdir_perror("netdir_getbyname() failed"); 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 ); }
if( t_listen( listen_fd, listen ) < 0 ) { t_error("t_listen failed"); exit( 6 ); }
res_fd = listen_fd; if( t_accept( listen_fd, res_fd, listen ) < 0 ) { if(t_errno == TLOOK) { if(( t_look( listen_fd )) == T_DISCONNECT ) { if( t_rcvdis( listen_fd, NULL ) < 0 ) { t_error("t_rcvdis failed"); break; } } } }
run_server( listen_fd ); t_free( (char *)listen, T_CALL ); t_free( (char *)bind, T_BIND ); netdir_free( srcaddr, ND_ADDRLIST ); t_close( listen_fd ); continue; } exit( 0 ); }
void conn_release( int signo ) {
if( t_look( listen_fd ) != T_DISCONNECT ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); } exit( 0 ); }
void run_server( int fd ) {
FILE *logfp = NULL; int n = 0; char buf[READBUFSIZE];
if((logfp = fopen( "logfile", "r")) == NULL ) { perror("cannot open logfile"); exit( 20 ); }
signal( SIGPOLL, conn_release ); if( ioctl( fd, I_SETSIG, S_INPUT ) < 0 ) { perror("cannot open logfile"); exit( 21 ); }
if( t_look( fd ) != 0 ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); }
while(( n = fread( buf, 1, READBUFSIZE, logfp )) > 0 ) { if( t_snd( fd, buf, n, 0 ) < 0 ) { t_error( "t_snd failed" ); exit( 23 ); } }
if( t_snddis( fd, NULL ) < 0 ) { t_error("t_snddis failed" ); exit( 24 ); } return; }
ここでは、HDLC-TLIのNRMサービスについて述べます。
NRMのサービスは、プロトコル上親子関係があり、P局(Primaly Station)側が主体となって動作することが決められているため、S局(Secondary Station)側からのコネクション確立等はできません。このため、ユーザからはクライアント、サーバの区分けはできません。したがって、NRMサービスでは両局とも必ずコネクション動作(t_connect(3N))を行うものとします。以降ABMサービスとの相違点を中心にインタフェースを示します。
まずNRMサービスでは、t_openにより特殊ファイル/dev/tpi/hdlcnrmをオープンします。その次に、t_bindの用意として、t_allocによりt_bind構造体を獲得します。qlenには、必ず0を設定してください。アドレスの設定は必須ではありません。なおこの処理はt_openを除き、ABMモードの「8.2.3 ABMサービスの概説」におけるローカル管理の「クライアント」と同様です。
注意:
データリンク・アドレス情報の獲得は、ABMモードと同様にネットワーク・セレクションgetnetconfig(3N)とネーム・トゥ・アドレス・マッピングnetdir(3N)を使用して獲得します。詳細は、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。また、データリンク・アドレス構造体については、「8.2.4.2 データリンク・アドレス構造体」を参照してください。
NRMサービスのデータリンク・アドレスもABMサービスと同様に、局アドレスや交換網加入時に付与されるダイアル番号等を含むデータリンク・アドレス構造体の形式をとります。データリンク・アドレス構造体の形式については、ABMサービスの「8.2.3.2 データリンク・アドレス構造体」を参照してください。
t_bind時においてのアドレス構造体(struct hdlcaddr)の設定方法による、プロバイダの動作については、ABMサービスのクライアントと同様です。詳細は、「8.2.3.2 データリンク・アドレス構造体」の「クライアント」の項目を参照してください。
なおt_connect時、アドレス構造体には接続する相手のアドレス情報を設定しなければなりません。この指定は必須です。
NRMサービスでは、通信する双方からのt_connectにより、コネクションが確立されます。まず、t_allocによりt_call構造体を獲得します。t_call構造体内のaddrの設定するデータリンク・アドレスは、ABMモードと同様にネットワーク・セレクションと、ネーム・トゥ・アドレス・マッピングを用いて獲得することができます。詳細については、「8.4 ネットワーク・セレクションとネーム・トゥ・アドレス・マッピング」を参照してください。
またopt,udataは、ABMサービスと同様に、NRMサービスでもサポートしていません。
t_connectによって、プロバイダは該当するラインのコネクション処理を行います。この操作により、データ通信が可能となります。この処理は、ABMサービスの「8.2.3.3 コネシクョン確立」の「クライアント」と同様です。
注意:
NRMサービスでは、通信を行う双方の発呼動作によりコネクション確立を行うため、ABMサービスのサポートしている関数のうち、着信動作を行うt_listen,t_accept関数をサポートしていません。
NRMサービスのデータ転送の動作は、ABMサービスの「8.2.3.4 データ転送」と同様です。
NRMサービスのコネクション解放の動作は、ABMサービスの「8.2.3.5 コネクション解放」と同様です。
次の例は、「8.2.4 NRMサービスの概説」で説明したNRMサービスのサンプル・プログラムを示しています。
#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[] = "hdlcnrm"; struct netconfig *config = NULL; struct nd_hostserv hostserv; struct nd_addrlist *srcaddr = NULL; struct nd_addrlist *destaddr = NULL; struct t_bind *bind = 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/hdlcnrm", O_RDWR, NULL )) < 0 ) { t_error( "t_open failed" ); exit( 2 ); }
最初にt_openにより、NRMサービスの特殊ファイル/dev/tpi/hdlcnrmをオープンします。
/* * 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 ) { netdir_perror("netdir_getbyname() failed"); 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により自局アドレスを設定します。
/* * 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( 6 ); }
/* * 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( 7 ); }
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( 8 ); }
次に、netdir_getbyname(3N)により相手のアドレス情報を獲得します。さらに、t_allocによりt_call構造体を獲得して値を設定し、t_connectを発行します。t_connectの正常終了時にコネクションは確立されます。NRMサービスの場合は、必ず双方にて、このサンプルの処理動作が行わなければなりません。コネクションが確立された後のデータ転送動作は、ABMサービスと同様です。
ここでは、非ブロック(非同期)モードでのライブラリ・コールを使用した、イベント駆動型サーバ・アプリケーションのサンプル・プログラムを示します。
/* * TLI (for HDLC) test program * server */ #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <signal.h> #include <poll.h> #include <stropts.h> #include <tiuser.h> #include <netconfig.h> #include <netdir.h>
extern int t_errno; int listen_fd = 0;
#define READBUFSIZE 1024
void run_server( int listen_fd ); void conn_release( int signo );
void main(int argc, char *argv[]) {
char netid[] = "hdlcabm"; struct netconfig *config = NULL;
/* * get config entry */ if(( config = getnetconfigent( netid )) == NULL ) { printf( "getnetconfigent( %s ) failed\n", netid ); exit( 1 ); } while( 1 ) {
int res_fd = 0; struct nd_hostserv hostserv; struct nd_addrlist *srcaddr = NULL; struct t_bind *bind = NULL; struct t_call *listen = NULL; int event = 0;
/* * t_open() */ if(( listen_fd = t_open(config->nc_device,(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 ); }
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 ); }
res_fd = listen_fd; if(( t_accept( listen_fd, res_fd, listen )) < 0 ) { if( t_errno == TLOOK ) { if(( t_look( listen_fd )) == T_DISCONNECT ) { if( t_rcvdis( listen_fd, NULL ) < 0 ) { t_error( "t_rcvdis failed" ); exit( 8 ); } } } else { t_error( "t_accept failed (not TLOOK)" ); exit( 9 ); } } run_server( listen_fd ); } else { exit( 10 ); }
t_free( (char *)listen, T_CALL ); t_free( (char *)bind, T_BIND ); netdir_free( srcaddr, ND_ADDRLIST ); t_close( listen_fd ); continue; } }
void run_server( int fd ) {
FILE *logfp = NULL; int nbytes = 0; char buf[READBUFSIZE];
if(( logfp = fopen( "logfile", "r" )) == NULL ) { perror( "cannot open logfile" ); exit( 20 ); }
signal( SIGPOLL, conn_release ); if( ioctl( fd, I_SETSIG, S_INPUT ) < 0 ) { perror( "ioctl I_SETSIG failed" ); exit( 21 ); }
if( t_look( fd ) != 0 ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); } while(( nbytes = fread( buf, 1, READBUFSIZE, logfp )) > 0 ) { if( t_snd( fd, buf, nbytes, 0 ) < 0 ) { t_error( "t_snd failed" ); exit( 23 ); } }
if( t_snddis( fd, NULL ) < 0 ) { t_error( "t_snddis failed" ); exit( 27 ); } exit( 28 ); }
void conn_release( int signo ) {
if( t_look( listen_fd ) != T_DISCONNECT ) { fprintf( stderr, "t_look: unexpected event\n" ); exit( 22 ); } exit( 0 ); }
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); } }
ここでは、HDLC-TLIを使用する上での注意事項と、HDLCプロトコル上の制限を受ける関数、およびその内容を示します。
引数のresfdとfdとが、別のエンドポイントが指定された場合(すなわちresfdとfdが等しくない場合)、本関数はエラー終了し、t_errnoにTBADSEQを設定します。この関数は、NRMサービスではサポートされません。t_call構造体のopt,udataおよびsequenceは、サポートされません。
t_bind構造体のaddrは、データリンク・アドレスを指定します。HDLC-TLIでは、データリンク・アドレスは、データリンク・アドレス構造体(struct hdlcaddr)の形式をとります。アドレス構造体内の設定方法により、プロバイダの動作や通知アドレス値(*ret)が異なります。データリンク・アドレス構造体については、各サービス・モードの「データリンク・アドレス構造体」を参照してください。
サーバでは、この関数の延長でプロバイダが回線のイネーブル処理を行ないます。
t_call構造体のaddrは、データリンク・アドレスを指定します。HDLC-TLIでは、データリンク・アドレスは、データリンク・アドレス構造体(struct hdlcaddr)の形式をとります。sndcallにおける、アドレス構造体の設定は必須です。
t_bind時にアドレス構造体を設定せず、特定ラインにbindされていない場合、プロバイダは本関数にて指定された相手局ネットワークIDと同じネットワークIDをもつラインの中から1つを選択し、コネクションの確立処理が行われます。
t_bind時にアドレス構造体を設定し、特定ラインにbindされている場合、本関数で指定する相手局ネットワークIDは、t_bind時に指定したものと同じネットワークIDでなければなりません。
t_call構造体のopt,udataおよびsequenceは、サポートされません。
クライアントでは、この関数の延長でプロバイダが回線のイネーブル処理を行ないます。
t_info構造体内には、以下の値が設定されます。
addr
データリンク・アドレス情報(データリンク・アドレス構造体)のバイト数。
options
オプション情報設定エリアのバイト数。
tsdu
トランスポート・コネクションを通じて送信されるメッセージの最大サイズ。HDLC-TLIでは、bindされてラインが特定するまでは、デフォルト値の16384バイトが設定され、ライン特定後は、そのラインのconfig情報として定義されている、最大送信フレーム長 - 2バイトが設定されます。
etsdu
0(この値を参照し、使用してはいけません。)
connect
-2(未サポート)
discon
-2(未サポート)
servtype
T_COTS 現在のサービスは、コネクション型サービスです。
この関数は、NRMサービスではサポートされません。
ラインごとに定義したデフォルト・パス制御情報(/etc/opt/FJSVwan/etc/hdlc/parameters)と異なる設定値で通信を行うアプリケーションは、本関数を使用してHDLCプロバイダに通知しなければなりません。ABMサービスにおいて、t_listenにより相手からの接続要求を受け付ける処理を行なう時にオプションをネゴシエートする場合、本関数はt_bindを行なう前に呼び出さなければなりません。
注意:
ユーザが、オプション情報を使用する場合は、必ずnetdir_options(3N)を使用してください。
netdir_optionsを使用して獲得したオプション・バッファのフォーマットを、図8.6に示します。fopthdr構造体については、<fnetif.h>に定義されています。
図8.6 netdir_optionsで得られるオプション・バッファのフォーマット
オプション・フラグ(flags)、優先データは、ともにサポートされません。
t_call構造体のopt,udataおよびsequenceは、サポートしていません。
オプション・フラグ(flags)、優先データは、ともにサポートされません。
この関数は、送信要求したデータがプロバイダに受け付けられた時点で復帰します。したがって、この関数の復帰と、回線上へのデータ送出とは同期していません。
送信要求されたデータの大きさが、t_info構造体のTSDUで示す値(そのラインのconfig情報として定義されている、最大送信フレーム長 - 2バイト)よりも大きかった場合、TSDUの大きさで分割された複数のデータとして送信されますので、注意してください。
本関数を発行するタイミングにより、t_errnoにTOUTSTATEが設定されて異常終了することがあります。この時、続けて再接続処理を行う場合は、必ず一度t_close()を行って、t_open()からリカバリを行ってください。
また、t_rcvdis()が異常終了するため、知ることができない切断理由コード(reason)は、ioctl(HDLC_GREASON)を使用することによって知ることができます。詳細は、「8.2.7 HDLC-TLI固有インタフェース」を参照してください。
HDLC-TLIでは、次のHDLCプロトコル固有制御インタフェースを追加しています。ここでは、次の機能について説明します。
次のHDLC-TLIのストリームioctl独自関数により、以下の機能がサポートされています。
HDLC_GREASON
相手からの切断、あるいは回線上のエラーによる切断の場合のdisconnect情報を読み出します。
以上の独自ioctlを呼び出す場合には、必ず、disconnect情報設定領域(int)をstrioctl構造体に設定しなければなりません。disconnect情報設定領域に設定される値は、<fnet_discreason.h>で定義されています。
以下に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 */ };
さらに、ioctl(HDLC_GREASON)のサンプルを示します。
/* * ioctl(HDLC_GREASON) */ #include <stropts.h> #include <fnetif.h> ・ ・ int fd; struct strioctl sti; int code = 0; ・ ・ /* * Connection was disconnected */ ・ ・ sti.ic_cmd = HDLC_GREASON; sti.ic_timout = 0; sti.ic_len = sizeof(int); sti.ic_dp = (char *)&code;
if( (ioctl( fd, I_STR, (char *)&sti )) < 0 ) { perror("ioctl(HDLC_GREASON) failed\n"); exit( 1 ); }
printf("DISCON_reason = 0x%x\n", code );
ioctlの第1引数のfdは、t_open()して得たファイル識別子です。このioctlは、相手から非同期に切断された場合に呼び出します。例えば、「8.2.3.7 プログラム例」のクライアント側のような一方向の通信中に、受信側でt_rcvが異常終了(TLOOKが通知された)し、t_look()の結果がT_DISCONNECTであった場合などです。
また、t_listen()がTLOOKで異常終了し、t_look()の結果がT_DISCONNECTであった場合にも呼び出すことができます。
復帰時には、切断時のdisconnect情報(切断要因等)が設定されています。
このioctlは、正常終了時には0が、異常終了時には-1が返ります。なお、ioctlの詳細については、streamio(7I)を参照してください。
目次
![]() ![]() |