クライアントが t_connect(3NSL) を呼び出した場合、サーバーのトランスポートエンドポイントへ接続要求が送られます。サーバーは各クライアントの接続要求を受け付け、接続のサービスを提供するため処理生成を行います。
if ((call = (struct t_call *) t_alloc(listen_fd, T_CALL, T_ALL)) == (struct t_call *) NULL) { t_error("t_alloc of t_call structure failed"); exit(5); } while(1) { if (t_listen( listen_fd, call) == -1) { t_error("t_listen failed for listen_fd"); exit(6); } if ((conn_fd = accept_call(listen_fd, call)) != DISCONNECT) run_server(listen_fd); }
サーバーは t_call 構造を割り当て、閉ループを行います。ループは接続要求のため t_listen(3NSL) でブロックします。要求が着信した時点で、サーバーは接続要求を受け付けるため accept_call() を呼び出します。accept_call() は接続を代替トランスポートエンドポイントで (下記で説明されている方法で) 受け付け、エンドポイントのハンドルを返します (conn_fd はグローバル変数)。接続が代替エンドポイントによって受け付けられるため、サーバーは引き続き元のエンドポイントの待機を行うことが可能です。呼び出しがエラーなしに受け付けられた場合、run_server が接続のサービスを提供するために起動されます。
XTI/TLI ではこのような処理のブロックを防ぐルーチンのために非同期モードをサポートします (「拡張機能」を参照)。
接続要求が着信すると、サーバーは例 3-6 で示されるようにクライアントの要求を受け付けるため accept_call() を呼び出します。
このサーバーは一度に 1 つの接続要求の処理しか行う必要がないことを暗黙の前提としています。これは通常のサーバーではあまりない状況です。複数の接続要求の同時処理を行うために必要なコードは XTI/TLI のイベントメカニズムのため、複雑です (このようなサーバーの場合は、「高度なプログラム例」を参照)。
accept_call(listen_fd, call) int listen_fd; struct t_call *call; { int resfd; if ((resfd = t_open("/dev/exmp", O_RDWR, (struct t_info *) NULL)) == -1) { t_error("t_open for responding fd failed"); exit(7); } if (t_bind(resfd,(struct t_bind *) NULL, (struct t_bind *NULL)) == -1) { t_error("t_bind for responding fd failed"); exit(8); } if (t_accept(listen_fd, resfd, call) == -1) { if (t_errno == TLOOK) { /*切断である必要あり*/ if (t_rcvdis(listen_fd,(struct t_discon *) NULL) == -1) { t_error("t_rcvdis failed for listen_fd"); exit(9); } if (t_close(resfd) == -1) { t_error("t_close failed for responding fd"); exit(10); } /*上に戻り、他の呼び出しの待機*/ return(DISCONNECT); } t_error("t_accept failed"); exit(11); } return(resfd); }
accept_call() には 2 つの引数があります。
listen_fd | 接続要求が着信したトランスポートエンドポイントのファイルハンドル |
call | 接続要求に関するすべての情報が格納された t_call 構造体を指す |
サーバーはトランスポートプロバイダのクローンのデバイス特殊ファイルを開き、アドレスをバインドすることによって、最初に別のトランスポートエンドポイントをオープンします。NULL はプロバイダによってバインドされたアドレスを返さないよう指定します。新しいトランスポートエンドポイント、resfd がクライアントの接続要求を受け付けます。
t_accept(3NSL) の最初の 2 つの引数は待機を行うトランスポートエンドポイントおよび接続が受け付けられるエンドポイントをそれぞれ指定します。待機を行うエンドポイント上で接続を受け付けると、接続の間、他のクライアントはサーバーへアクセスすることができません。
t_accept(3NSL) の 3 つ目の引数は、接続要求を格納している t_call 構造体を指します。この構造体は呼び出しを行っているユーザーのアドレス、および t_listen(3NSL) によって戻されたシーケンス番号が格納されています。サーバーが複数の接続要求の待機を行った場合、シーケンス番号は重要です。「拡張機能」ではこの例を説明しています。t_call 構造体はプロトコルオプションおよびクライアントに渡すユーザーデータも識別します。このトランスポートプロバイダはプロトコルオプションまたは接続中のユーザーデータの転送をサポートしないため、t_listen(3NSL) によって戻された t_call 構造体は変更なしに t_accept(3NSL) へ渡されます。
この例は簡略化されています。サーバーは t_open(3NSL) または t_bind(3NSL) 呼び出しが失敗した時点で終了します。exit(2) は listen_fd のトランスポートエンドポイントを閉じ、クライアントへは切断要求が送信されます。クライアントの t_connect(3NSL) 呼び出しは失敗し、t_errno は TLOOK に設定されます。
t_accept(3NSL) は、待機しているエンドポイント上で接続が受け付けられる前に非同期イベントが発生し、t_errno が TLOOK に設定された場合失敗する可能性があります。この状態では待機を行っている接続要求が 1 つでも、切断要求のみが送信可能であることが表 3-8 で示されています。このイベントはクライアントが事前に行った接続の要求を取り消した場合に発生する可能性があります。切断要求が着信した場合、サーバーは t_rcvdis(3NSL) を呼び出し応答を行う必要があります。このルーチンの引数は、切断要求のデータを取り出すために使用される t_discon 構造体へのポインタです。この例ではサーバーは NULL を渡します。
切断要求を受け取った後、accept_call は応答を行うトランスポートエンドポイントを閉じ、 DISCONNECT を戻してサーバーに接続がクライアントによって切断されたことを伝えます。その後サーバーは他の接続要求の待機を行います。
図 3-4 はサーバーの接続確立の方法を示します。
トランスポートの接続は新しく応答を行うエンドポイント上で確立され、待機を行ったエンドポイントは他の接続要求の待機を行うため解放されます。