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

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

8.2 HDLC API

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

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

8.2.1 HDLC-TLIの概要

コネクション・モードHDLC-TLIは、アプリケーションに対して、TLIと同様のインタフェースを提供することを目的としています。
このため、データリンク・プロバイダ・インタフェースをトランスポート・プロバイダ・インタフェースにマッピングしています。

図8.2にHDLC-TLIの概要を示します。

image

図8.2 HDLC-TLIの概要

HDLCインタフェース・プロバイダは、上位にデータリンク・レイヤ・インタフェースを提供します。
これをTLI変換モジュールがトランスポート・レイヤ・インタフェースに変換することにより、ユーザに対してHDLC-TLIインタフェースを提供します。
したがって、データリンク層のサービスはトランスポート層のサービスにマッピングされ、データリンク・エンドポイント(データリンク・コネクションの端点)はトランスポート・エンドポイントにマッピングされています。
以降の記述では、基本的にデータリンク層がトランスポート層にマッピングされていることを前提に、トランスポートのインタフェース用語で記述します。

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

image

図8.3 マッピングモデル

表8.1 トランスポート層用語に対応するデータリンク層用語一覧

本文中の記述

マッピング対象

意味

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

データリンク・エンドポイント

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

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

データリンク・コネクション

HDLCで独立の通信を行える最小単位であり、NRMまたはABMに対応します。

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

コンバート・モジュール+HDLCプロバイダ

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

8.2.2 HDLC-TLIのサービス・モード

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

ABMはHDLC-ABM手順を使用するものであり、NRMはHDLC-NRM手順を使用するものです。

8.2.2.1 ABMサービス

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

◆ローカル管理

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

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

image

図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に示されるように、各ユーザが相互の間にコネクションを生成することができます。

image

図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

コネクションを打ち切るか、または接続要求を拒否する。

8.2.2.2 NRMサービス

NRMのサービスは、プロトコル上親子関係があり、P局(Primaly Station)側が主体となって動作することが決められているため、S局(Secondary Station)側からのコネクション確立等はできません。このため、HDLC-TLIユーザからの発着信を指定することはできません。

ユーザは、NRMサービスで通信を行う双方において、必ずコネクト動作(t_connect)を行わなければなりません。したがってNRMサービスでは、ABMサービスがサポートする関数のうち、次の関数はサポートしません。

8.2.2.3 状態の遷移

HDLC-TLIは次の2つの構成要素を持ちます。

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

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

8.2.3 ABMサービスの概説

ここでは、HDLC-TLIのABMサービスについて述べます。前節で説明したように、ABMサービスはクライアント/サーバのモデルを用いて例示することができます。2つのプログラミング例を用いて、ABMサービスの重要な概念を提示します。これらの例は関連しており、1つの例はクライアントがどのようにサーバへのコネクションを確立し、そのサーバと通信するかを例示し、もう1つの例は対となるサーバ側を示します。ここで示す例は、「プログラム例」でその全体が示されます。
これらの例では、クライアントは、サーバ・プロセスとのコネクションを確立します。サーバは、その時にクライアントにファイルを転送します。次にクライアントは、サーバからのデータを受信して、そのデータをクライアントの標準出力ファイルに書き込みます。

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

8.2.3.2 データリンク・アドレス構造体

データリンク・アドレスは、局アドレスや交換網加入時に付与されるダイアル番号等を含むデータリンク・アドレス構造体の形式をとります。次にデータリンク・アドレス構造体(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 によるアドレス構造体の作成を行ってください。

8.2.3.3 コネクション確立

コネクション確立の手順では、クライアントとサーバの動作の相違点がはっきりしています。
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はエラー終了します。

8.2.3.4 データ転送

一度コネクションが確立されると、クライアントとサーバは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によって認識することができるからです。

8.2.3.5 コネクション解放

データ転送中の任意の時点で、どちらかのユーザがトランスポート・コネクションを解放して通信を終了することができます。
すでに述べたように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により自分のトランスポート・エンドポイントをクローズします。

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

8.2.3.6 事象の処理

エラー・コード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.7 プログラム例

◆ABMクライアント

次の例は、「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 );
}

◆ABMサーバ

次の例は、「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;
}

8.2.4 NRMサービスの概説

ここでは、HDLC-TLIのNRMサービスについて述べます。

NRMのサービスは、プロトコル上親子関係があり、P局(Primaly Station)側が主体となって動作することが決められているため、S局(Secondary Station)側からのコネクション確立等はできません。このため、ユーザからはクライアント、サーバの区分けはできません。したがって、NRMサービスでは両局とも必ずコネクション動作(t_connect(3N))を行うものとします。以降ABMサービスとの相違点を中心にインタフェースを示します。

8.2.4.1 ローカル管理

まず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 データリンク・アドレス構造体」を参照してください。

8.2.4.2 データリンク・アドレス構造体

NRMサービスのデータリンク・アドレスもABMサービスと同様に、局アドレスや交換網加入時に付与されるダイアル番号等を含むデータリンク・アドレス構造体の形式をとります。データリンク・アドレス構造体の形式については、ABMサービスの「8.2.3.2 データリンク・アドレス構造体」を参照してください。
t_bind時においてのアドレス構造体(struct hdlcaddr)の設定方法による、プロバイダの動作については、ABMサービスのクライアントと同様です。詳細は、「8.2.3.2 データリンク・アドレス構造体」の「クライアント」の項目を参照してください。
なおt_connect時、アドレス構造体には接続する相手のアドレス情報を設定しなければなりません。この指定は必須です。

8.2.4.3 コネクション確立

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関数をサポートしていません。

8.2.4.4 データ転送

NRMサービスのデータ転送の動作は、ABMサービスの「8.2.3.4 データ転送」と同様です。

8.2.4.5 コネクション解放

NRMサービスのコネクション解放の動作は、ABMサービスの「8.2.3.5 コネクション解放」と同様です。

8.2.4.6 プログラム例

◆NRMトランザクション

次の例は、「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サービスと同様です。

8.2.5 より高度な機能

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

8.2.5.1 プログラム例

/*
 *      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);
        }
}

8.2.6 HDLC-TLI拡張インタフェース

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

8.2.6.1 HDLC-TLI関数においての注意事項

8.2.6.2 HDLCプロトコルの制限を受ける関数

◆t_accept関数

引数のresfdfdとが、別のエンドポイントが指定された場合(すなわちresfdfdが等しくない場合)、本関数はエラー終了し、t_errnoにTBADSEQを設定します。この関数は、NRMサービスではサポートされません。t_call構造体のopt,udataおよびsequenceは、サポートされません。

◆t_bind関数

t_bind構造体のaddrは、データリンク・アドレスを指定します。HDLC-TLIでは、データリンク・アドレスは、データリンク・アドレス構造体(struct hdlcaddr)の形式をとります。アドレス構造体内の設定方法により、プロバイダの動作や通知アドレス値(*ret)が異なります。データリンク・アドレス構造体については、各サービス・モードの「データリンク・アドレス構造体」を参照してください。
サーバでは、この関数の延長でプロバイダが回線のイネーブル処理を行ないます。

◆t_connect関数

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_getinfo関数

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

addr

データリンク・アドレス情報(データリンク・アドレス構造体)のバイト数。

options

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

tsdu

トランスポート・コネクションを通じて送信されるメッセージの最大サイズ。HDLC-TLIでは、bindされてラインが特定するまでは、デフォルト値の16384バイトが設定され、ライン特定後は、そのラインのconfig情報として定義されている、最大送信フレーム長 - 2バイトが設定されます。

etsdu

0(この値を参照し、使用してはいけません。)

connect

-2(未サポート)

discon

-2(未サポート)

servtype

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

◆t_listen関数

この関数は、NRMサービスではサポートされません。

◆t_optmgmt関数

ラインごとに定義したデフォルト・パス制御情報(/etc/opt/FJSVwan/etc/hdlc/parameters)と異なる設定値で通信を行うアプリケーションは、本関数を使用してHDLCプロバイダに通知しなければなりません。ABMサービスにおいて、t_listenにより相手からの接続要求を受け付ける処理を行なう時にオプションをネゴシエートする場合、本関数はt_bindを行なう前に呼び出さなければなりません。

注意:
ユーザが、オプション情報を使用する場合は、必ずnetdir_options(3N)を使用してください。

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

image

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

◆t_rcv関数

オプション・フラグ(flags)、優先データは、ともにサポートされません。

◆t_rcvconnect関数

t_call構造体のopt,udataおよびsequenceは、サポートしていません。

◆t_snd関数

オプション・フラグ(flags)、優先データは、ともにサポートされません。
この関数は、送信要求したデータがプロバイダに受け付けられた時点で復帰します。したがって、この関数の復帰と、回線上へのデータ送出とは同期していません。
送信要求されたデータの大きさが、t_info構造体のTSDUで示す値(そのラインのconfig情報として定義されている、最大送信フレーム長 - 2バイト)よりも大きかった場合、TSDUの大きさで分割された複数のデータとして送信されますので、注意してください。

◆t_rcvdis関数

本関数を発行するタイミングにより、t_errnoにTOUTSTATEが設定されて異常終了することがあります。この時、続けて再接続処理を行う場合は、必ず一度t_close()を行って、t_open()からリカバリを行ってください。
また、t_rcvdis()が異常終了するため、知ることができない切断理由コード(reason)は、ioctl(HDLC_GREASON)を使用することによって知ることができます。詳細は、「8.2.7 HDLC-TLI固有インタフェース」を参照してください。

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)を参照してください。


目次 前ページ次ページ

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