メインコンテンツまでスキップ

III - ネットワーク制御プログラム

各ホストはネットワーク制御プログラム (NCP) と呼ばれるモジュールを実装し、これがそのホストに関わるすべてのネットワーク通信を制御する。NCP のネットワークは、個々のプロセス間の通信経路を実装する分散通信システムを形成する。NCP プロトコルの課題は次のものである: (i) これらの通信経路の定義、および (ii) これらの通信経路を維持するうえで分散 NCP システムを協調させる仕組み。これらを以下で論じる。

socket​

2 つのプロセス間の通信は、2 つの socket 間の単方向接続によって行われる: 一方のプロセスに接続された送信 socket と、他方のプロセスに接続された受信 socket である。socket は次の特徴を持つ:

socket 識別子 - socket をネットワーク全体で一意に識別するために socket 識別子が用いられる。これは 48 ビットから成り、次の構成要素を持つ:

  1. ユーザー番号 (24 ビット) - プロセスに接続された socket は、そのプロセスに属するものとしてユーザー番号によって識別される。このユーザー番号は、8 ビットの "ホーム" ホストコードと、ホームホストが割り当てた 16 ビットのユーザーコードから成る。このユーザー番号は、どのホストにあるそのユーザーのいずれのプロセスに接続された socket についても同じである。

  2. インスタンスタグ (8 ビット) - 一人のユーザーに属する複数のプロセスが単一のホスト内に同時に存在し得る。インスタンスタグは、socket が属する特定のプロセスを識別する。慣例により、あるホストにおいてそのユーザーが最初にネットワークを使用するプロセスはインスタンスタグ = 0 を受け取る。

  3. ホスト番号 (8 ビット) - これは、接続されたプロセスが存在するホストのコードである。

  4. socket コード (8 ビット) - このコードは各プロセスに 128 個の送信 socket と 128 個の受信 socket を提供する。最下位ビットが、これが "送信" (= 1) か "受信" (= 0) の socket かを決める。

socket の状態 - 各 socket には関連する状態がある。NCP は socket のより一時的な状態を実装してもよいが、次の 3 つが概念上重要である。

  1. 非活動 - この socket を listen したいと NCP に告げたプロセスが現在存在しない。他のいかなるプロセスも非活動の socket と通信することはできない。

  2. オープン - あるプロセスがこの socket に関するイベントを listen することに同意したが、まだ接続されていない。

  3. 接続済み - この socket は現在別の socket に接続されている。

socket イベントキュー - オープンまたは接続済みの各 socket について、それを所有するプロセスに開示されるべきイベントの待ち行列が維持される。これは、この socket を接続または切断しようとする 1 つ以上の外部プロセスの動作によって生成された特定のイベントを、時系列順に並べたリストから成る。イベントキューの項目は、イベント種別と、関係する外部 socket の識別子から成る。次のイベント種別が定義される:

  1. "request" - 外部 socket が接続を要求する。(ローカル socket が既に接続されている場合は待ち行列に入れない)

  2. "accept" - 外部 socket が要求された接続を受け入れる。

  3. "reject" - 外部 socket が要求された接続を拒否する。

  4. "close" - 外部 socket が既存の接続を切断する。

"request" イベントは、それが受け入れられるか拒否されると待ち行列から取り除かれる。他のイベントは、所有するプロセスに開示されるときに待ち行列から取り除かれる。

socket を所有するプロセスに対して透過的であることが意図されたイベントもあり、それらはイベントキューに項目を生成しない。

イベントキューは概念上無制限であるが、その長さに何らかの実際的な制限を設ける必要があるように思われる。ある socket のイベントキューが一杯のときは、キューを増やすことになる到着イベントは破棄し、送信側 NCP に通知すべきである (後述の ERR コマンドによる)。

NCP 制御通信​

NCP ネットワークは、その個々の構成要素間で受け渡される制御コマンドによって活動を調整する。これらのコマンドは一般に、コマンドを受け取った NCP が制御する socket 接続の作成と操作に関する。制御コマンドは、リンク番号 1 (制御リンクとして指定される) を経由してメッセージとしてそのホストに送られることにより、特定の NCP に向けられる。このリンクはその目的のために予約されている。IMP ネットワークは、これらのメッセージと、socket 接続を通じた通信を実装する通常のデータメッセージとを区別しない。

次の NCP 制御コマンドが定義される:

  1. 接続要求

    RFC <local socket> <foreign socket> [<link no.>]

    NCP は、ローカル socket と外部 socket の間に接続を開始しようとして、このコマンドを外部 NCP に向ける。外部 socket がオープンであれば、外部 NCP は "request" イベントをその socket のイベントキューに入れ、所有するプロセスに開示できるようにする。外部プロセスが受け入れると、外部 NCP は別の RFC という形で肯定応答を返す。接続を拒否する場合は CLS コマンド (後述) を発行する。外部 socket がオープンでない (非活動または接続済み) 場合、所有するプロセスに問い合わせることなく RFC は自動的に拒否される。同じ socket への複数の RFC は、受信順にそのイベントキューに入れられる。所有するプロセスが接続を受け入れることを決めると、待ち行列に入っていた RFC はすべて NCP によって自動的に拒否される。接続され得る対のうち "受信" socket を制御している NCP が、メッセージが流れるリンク番号を指定する。

  2. 接続の閉鎖

    CLS <local socket> <foreign socket>

    NCP は、既存の接続を切断するため、または RFC に否定的に応答するために、このネットワークコマンドを発行する。NCP がローカルの送信 socket を閉じる場合、CLS コマンドがその socket 接続上の最後のメッセージより先に外部 NCP に到達する可能性があるため、競合問題が生じ得る。この競合は 2 つの基準を守ることで防がれる: (i) ローカル送信 socket に対する CLS コマンドは、外部 socket への最後のメッセージの RFNM が戻るまで送信されない、そして (ii) 外部 NCP は到着したすべてのメッセージを受信順に処理する。

  3. 接続上の出力のブロック

    BLK <foreign send socket>

    プロセスが受信 socket を通じてデータを読む速度が、メッセージが到着する速度より遅いことがあり、そのため NCP のバッファが詰まりがちになる。NCP はこのコマンドを外部 NCP に発行し、受信プロセスが追いつくまでその socket 対上のそれ以降の送信をブロックする。

  4. ブロックされた接続上の出力の再開

    RSM <foreign send socket>

    NCP は、以前にブロックされた接続のブロックを解除するためにこのコマンドを発行する。

  5. 接続に接続されたプロセスの中断

    INT <foreign socket>

    このメッセージを受け取ると外部 NCP は、<foreign socket> に接続され、かつローカル socket に接続されている外部プロセスを直ちに中断する。中断された接続を経て NCP ネットワーク内を既に伝送中のデータは、宛先 socket へ伝送される。"中断" の意味は、そのプロセスが現在の実行を直ちに打ち切り、何らかの標準的な手続きを実行するということである。その手続きは本プロトコルレベルでは定義されない。

  6. 外部 NCP への誤ったコマンドの報告

    ERR <code> <command length> <command in error>

    このコマンドは、偽のネットワークコマンドやメッセージ、あるいはコマンドの処理を妨げる過負荷状態を報告するために用いられる。<code> はエラー種別を指定する。<code> が誤ったネットワークコマンドを指定する場合、<command in error> はそのコマンド (IMP ヘッダを含まない) であり、<command length> はその長さをビットで示す整数である。<code> が誤ったメッセージを指定する場合、<command in error> はその誤ったメッセージが伝送されたリンク番号のみを含む。(これは NWG/RFC 40 の規定からわずかに変更されている。)

  7. ネットワーク試験コマンド

    ECO <48 bit code> <echo switch>

    NCP は、任意の <48 bit code> (socket 識別子と同じ長さ) と <echo switch> 'on' を持つ ECO コマンドを外部 NCP に向けることにより、自分とその外部 NCP との間の通信の品質を試験できる。そのような ECO コマンドを受け取った NCP は、同じ <48 bit code> と <echo switch> 'off' を持つ確認の ECO を、発信元 NCP へ直ちに送るべきである。NCP は <echo switch> 'off' の ECO には応答しない。このコマンドはネットワーク全体の初期調整にかなり役立つと我々は考える。

  8. 無操作コマンド

    NOP

    NCP はこのコマンドを受け取ると破棄する。

NCP へのユーザーインターフェース​

各ホストの NCP にはインターフェースがあり、ローカルプロセスは NCP の制御の下でそれを通じてネットワークを利用できる。このインターフェースの正確な仕様はネットワークプロトコルの問題ではない。各サイトがそれぞれの要件に合わせた独自のインターフェースを持つからである。NCP へのユーザーインターフェースに対するプロトコル上の要件は、意図されたすべてのネットワーク機能を提供し、不正な特権を提供しないことである。そのような不正な特権の例には、別のプロセスになりすます能力、自分宛でない通信を盗聴する能力、あるいは NCP を誘導して偽のネットワークコマンドやメッセージを送出させる能力が含まれる。

ここでは Carr、Crocker、Cerf の提案に基づくインターフェースを概説する。これはネットワークを十分に活用するのに足りるものである。この特定の呼び出しの集合は主に例示を目的としているが、必要な機能の種類を示している。

次の NCP への呼び出しが利用できる:

  1. LISTEN <my 8 bit socket code>

    ユーザーはこの socket を開き、そのために空のイベントキューを作成する。この LISTEN 呼び出しは、最初の "request" イベントを待ってブロックするかもしれず、あるいは直ちに戻るかもしれない。

  2. INIT <my socket code> <foreign socket>

    ユーザーは <my socket> を <foreign socket> に接続しようと試みる。ローカル NCP は接続の作成を求める RFC を外部 NCP へ送る。返ってくる確認は RFC (要求が受け入れられた) か CLS (要求が拒否された) のいずれかである。呼び出し元の選択により、INIT 呼び出しは期待される "accept" または "reject" イベントでブロックするか、待たずに直ちに戻ることができる。後者の場合、ユーザーは外部 NCP の処置を判定するために、後で STATUS (後述) を呼び出さなければならない。ブロックされた INIT 呼び出しが戻ると、"accept" または "reject" イベントはイベントキューから取り除かれる。

  3. STATUS <my socket code>

    この呼び出しは、<my socket> の待ち行列の中で最も早い、まだ報告されていないイベントを報告する。その種別のイベントが開示によって削除可能であれば、STATUS 呼び出しはそのイベントを待ち行列から削除する。

  4. ACCEPT <my socket code>

    ユーザーは、"request" イベントが <my socket> のイベントキューの中で最も早い外部 socket との接続を受け入れる。確認の RFC が受け入れられた外部 socket へ送られ、"request" イベントはイベントキューから削除される。待ち行列に他の "request" イベントが存在する場合は、NCP は CLS コマンドを送出してそのイベントを削除することにより、自動的に接続を拒否する。

  5. REJECT <my socket code>

    ユーザーは、"request" イベントが <my socket> のイベントキューの中で最も早い外部 socket との接続を拒否する。NCP は CLS コマンドを送出し、"request" イベントを待ち行列から削除する。

  6. CLOSE <my socket code>

    ユーザーは NCP に対し、この socket への活動中の接続を切断し、その socket を非活動化するよう指示する。接続が存在していた場合、NCP は外部 socket へ CLS コマンドを送出する。"close" イベントが外部プロセスに開示されると、外部 socket の状態も閉鎖となる。

  7. INTERRUPT <my socket code>

    ユーザーは NCP に対し、<my socket> に接続された外部 socket へ INT コマンドを送出するよう指示する。

  8. TRANSMIT <my socket code> <pointer> <nbits>

    ユーザーは、<pointer> が指す領域へ <nbits> のデータを読み込む (<my socket> が受信の場合) か、そこから書き出す (<my socket> が送信の場合) ことを望む。書き込み呼び出しは、NCP が接続を経てメッセージを送るためにデータを待ち行列に入れた後、直ちに戻る。書き込み呼び出しがブロックするのは、接続がブロックされている場合、またはローカル NCP が負荷過大で要求を直ちに処理できない場合だけである。接続を経て伝送されるデータは、最大長 8095 ビットの 1 つ以上の IMP メッセージに整形され、受信側接続を制御する NCP が送った RFC で指定されたリンク番号を通じて外部ホストへ伝送される。<my socket> のイベントキューにある "close" イベントは、TRANSMIT の動作を通じて開示される。書き込み呼び出しは "close" イベントを直ちに開示する。読み込み呼び出しはすべてのデータが読み込まれたときにそれを開示する。

ユーザーの視点から見た接続の履歴​

例示​

ホスト A のプロセス 'a' が、ホスト B のプロセス 'b' との接続を確立したいものと仮定する。通信が行われる前に、2 つの条件が満たされなければならない:

  1. プロセス 'a' は、'b' の socket 空間のうち接続したい socket を、自分の NCP に指定できなければならない。

  2. プロセス 'b' は既にこの socket を LISTEN していなければならない。

1. 接続の確立​

  1. プロセス 'b' が socket 'Bb9' を LISTEN する。

  2. プロセス 'a' が 'Bb9' を自分の 'Aa12' へ INIT する。A の NCP はリンク番号 = 47 を指定する RFC を生成する。この番号は利用可能なリンクの集合から選ばれる。これが、接続がプロセス 'b' によって ACCEPT された場合にメッセージを受信するためのリンクである。

  3. プロセス 'b' は A の INIT 要求を知らされる。接続を REJECT してもよく (NCP B が CLS を返す)、ACCEPT してもよい (NCP B が RFC を返す)。

  4. プロセス 'b' が ACCEPT すれば、確認の RFC が接続を確立し、メッセージが流れるようになる。

          HOST  A               |          HOST B
INITIATOR | ACCEPTOR
PROCESS 'a' | PROCESS 'b'
|
|
| a. LISTEN 'socket code 9'
|
|
b. INIT 'socket code 12' 'Bb9' |
RFC 'AA12' 'Bb9' 'link 47' ==========>
|
| c. ACCEPT 'socket code 9'
| RFC 'Bb9' 'Aa12'
|
| d. TRANSMIT 'send buffer' 'len'
| 'socket 9'
<============== IMP message 'link 47' 'send buffer'
|
e. TRANSMIT 'rec buffer' 'length'
'socket 12' ============>
|
| f. CLOSE 'socket code 9'
|
last RFNM ===>
<============== CLS 'Bb9' 'Aa12'
closes socket 'Aa12' |
|

図 2: socket 接続の確立と通信

2. 接続を経たメッセージの送信​

  1. プロセス 'b' は、接続を通じてデータを送るために TRANSMIT 呼び出しを発行する。NCP B はこれを IMP メッセージに整形し、A の RFC で指定されたとおりリンク番号 = 47 で NCP A へ送る。

  2. NCP A は、リンク番号 = 47 で NCP B から生のメッセージを受け取る。NCP A はこのリンク番号を用いて意図された受信者が誰かを判断し、そのメッセージを受信プロセス用のバッファに格納する。

  3. プロセス 'a' は任意の時点で socket コード 12 に対する読み込み (TRANSMIT) 呼び出しを発行してよい。その socket に保留中のデータがなければ、読み込み呼び出しはブロックする。読み込み呼び出しは、socket コード 12 を経て伝送された指定数のビットを取り出す。それは IMP メッセージの境界をまたぐこともある。IMP メッセージの境界は読み込み呼び出しには見えない。

  4. プロセス 'b' がデータを接続経由で送る速度が、プロセス 'a' がそれを取り出す速度より速い場合、A のバッファが埋まり始めれば NCP A は NCP B へ BLK コマンドを発行できる。後にプロセス 'a' が追いついたとき、NCP A は RSM コマンドによって B に送信の再開を伝えられる。

3. プロセス 'b' が接続を閉じる​

  1. プロセス 'b' が接続を閉じることを決め、NCP B へ CLOSE 呼び出しを発行する。競合問題を避けるため、B はこの接続上の前のメッセージの RFNM を待ってから、CLS コマンドを NCP A へ送る。CLS コマンドメッセージの RFNM が戻ると、NCP B は socket 'Bb9' を自分の表から消去し、自分側での閉鎖を完了して 'Bb9' を非活動化する。

  2. NCP A 内では順次処理が行われるため、NCP B からの CLS が届く前に、socket 'Aa12' への最後のメッセージがプロセスへ向けられたことが保証される。B からの CLS を受け取ると、NCP A は socket 'Aa12' を "close pending" と印し、"close" イベントを 'Aa12' のイベントキューに入れる。

  3. バッファされたデータが保留されている間、プロセス 'a' は socket 'Aa12' に対する読み込み呼び出しを依然として発行できる。バッファが空になった後に 'a' が読み込み呼び出しを発行すると、閉鎖を 'a' に知らせるために "close" イベントが開示され、socket 'Aa12' は NCP A の表から消去される。

4. プロセス 'a' が接続を閉じる​

  1. ステップ 2 に戻り、プロセス 'a' が自分側から接続を閉じたいものと仮定する。'a' がいったん CLOSE 呼び出しを発行すれば、もはやその socket を経てメッセージを読みたいとは思わないと仮定するので、競合問題はない。

  2. プロセス 'a' が socket 'Aa12' に対して CLOSE 呼び出しを発行すると仮定する。NCP A は直ちに CLS コマンドを NCP B へ送出し、socket 'Aa12' を "close pending" と印す。'Aa12' で読み込み用にバッファされていたデータは破棄される。プロセス 'b' から既に送信され途中にある残りのメッセージが IMP ネットワークを抜けて NCP A に達し、エラー通知なしに破棄されるようにするため、NCP A は CLS コマンドの RFNM を受け取った後、適当な期間にわたって 'Aa12' を自分の表に保持する。この期間中、NCP A は閉鎖中の接続を経て受信したすべてのメッセージを破棄する。これらの死んだメッセージが到着するのに合理的な時間を置いた後、NCP A は 'Aa12' を自分の表から消去し、事実上接続を閉じて 'Aa12' を非活動化する。その後 socket 'Aa12' へのメッセージは、NCP A が発信元 NCP へ ERR "erroneous command" を送る結果となる。

  3. NCP B が CLS コマンドを受け取ると、socket 'Bb9' は "close pending" と印され、CLS イベントが 'Bb9' のイベントキューに入れられる。次にプロセス 'b' がその socket に書き込もうとしたとき、閉鎖を知らせるために CLS イベントが開示され、socket 'Bb9' は NCP B の表から取り除かれる。