Part2 通信プロトコルの仕様

最終更新: 1998-10-02

2.1 XMODEM

XMODEMはネット黎明期に急速に広まった通信プロトコルである。非常に簡単な仕様でバイナリデータの転送が実現できるので、多くのコンピュータ、ソフトウェアに実装されている。XMODEMは確かに多くの欠点も合わせ持っているが、簡単に実装できるというメリットがそれにも勝る魅力となっている。

XMODEMは、データをブロックと呼ばれる単位で送信する。ブロックの構造は図1のようになっている。[SOH]はヘッダー開始を意味するキャラクターで、0x01である。シーケンス番号で、1から始めて255になると次は0にリセットされ、その後また1から順に数えることになる。 シーケンス番号の補数は、シーケンス番号が回線のノイズなどでエラーになった時に検出できるように用意されているもので、シーケンス番号と補数を加えると0xffになるような値の1バイト文字である。

データは128バイトの長さで、8ビットのバイナリの情報をそのまま格納している。従って、XMODEMで送信したデータは、必ず長さが128の倍数バイトになる。元データが128で割り切れない時には、余ったバイトをControl-Zで埋めなければならない。 XMODEMのデータ構造には2つの欠点がある。まず、最後のデータの余りをControl-Zで埋めるため、偶然データの最後がControl-Zである場合との区別ができない。また、データの元々のサイズの情報は失われる。もう一つの欠点は、バイナリのデータをそのまま転送することになるため、回線は8ビットのデータ全てをそのまま通過するように設定されていなければならない。例えば、XON/XOFF制御を有効にしたままでXMODEMを使ってデータ転送をしようとすると、Control-S、Control-Qが偶然データ中に含まれていた場合の動作に問題が出てくる。後発の高機能のファイル転送プロトコルは、この問題はクオートによって回避している。

チェックサムは、データ部分を全て加算して、256で割った余りである。即ち、1バイトのレジスタを使って単純に加算した結果に等しい。
------------------------------------------------------------------------
図1
XMODEM のブロック構成

[SOH] [シーケンス番号] [シーケンス番号の補数] [データ] [チェックサム]
------------------------------------------------------------------------

2.1.1 XMODEM の手順

まず、XMODEMによるファイル転送を行うことが決まれば、送信側は受信側からNAKが送られてくるのを1分間待つ。送信側は、これが送られてくるまで待つことになるが、NAKまたはCAN以外のキャラクタは全て無視する。CANは、XMODEMの処理を中断するための指示であり、これを受け取れば送信側は即時処理を中断する。

送信側はNAKを受け取ると、ただちに最初のブロックを送信し始める。受信側はNAKを送ると、最初のブロックが到着することを期待して待っている。しかし何らかのトラブルにより、応答がないことがある。この場合、10秒待っても何のデータも来ないならば、再度NAKを送信する。さらに10秒待ってもデータが来なければ、もう一度NAKを送る。これを10回繰り返してもダメなら、XMODEMによる転送を諦めることになる。

送信側は、ブロックを1つ送信し終えたら、それに対する応答が戻ってくるのを待つ。受信側は、シーケンス番号、チェックサムを確認して、データが正しいと判断した場合には、ACKを送信する。チェックサムが合わない場合には、NAKを送信する。送信側は、ACKを受け取った場合には、次のブロックを送信する。NAKを受け取った場合には、今送ったブロックをもう一度送信する。

送信側は、最後のブロックに対するACKを受け取ると、EOTを送信する。EOTが、ファイル転送終了の合図になる。受信側は、EOTを受け取った時点でACKを送信して、ファイルをクローズしてXMODEMによる転送処理を終える。

処理の中断はCANを送信することにより実現される。送信側の都合でデータを送ることができなくなったら、ブロックの代わりにCANを送信する。受信側でエラーが発生したり、途中のブロックが消滅して処理が続行できない場合には、ACKではなくCANを送信する。

受信側でエラーと判断できる場合としては、以下のものが考えられる。

  1. シーケンス番号とシーケンス番号の補数の和が255にならなかった場合。この場合はいずれかが正しくないのであるが、どちらが正しいともいえないのだからNAKを返送して再度ブロックを送ってもらうことになる。
  2. 既に正常に受信したブロックと同じシーケンス番号のブロックが送られて来た場合。例えばACKが途中で化けて送信側で認識されなかった場合にこのようになる。ブロックは既に受信しているのだから、二度目に受け取ったブロックは無視してACKを送信すれば、送信側は次のブロックを送ってくるはずである。
  3. 次に受信すべきブロックが受け取れず、さらに先のブロックが送られて来た場合。途中のブロックを受け取り損ねた場合である。このエラーの場合には、XMODEMは処理を続行することができない。受信側は処理を中断するために、CANを続けて2回送信する。
  4. データが送られてこない場合。回線の事情で、途中でデータが消失した場合には、いくら待ってもデータが送られてこないはずである。10秒待っても期待したデータが到着しない場合には、NAKを送り、再度の送信を促す。さらにデータが送られてこない場合には、10回までこれを繰り返す。
  5. 何かのデータが化けたために誤ったEOTを受け取ってしまった場合。受信側はEOTを受け取ったらファイルをクローズして処理を終了してしまうので、データ送信がまだ終了していないのに、EOTがやって来たと誤解すると、そこで処理を放棄せざるを得ない結果となる。これを避けるために、EOTを受信した時には、まずNAKで応答する方法が用いられることがある。送信側がNAKを受け取って、再度EOTを送信してくれば、それをもってEOTが確かに送られてきたとみなすのである。

送信側でエラーと判断できる場合としては、受信側からACKもNAKも戻ってこない場合が考えられる。10秒待ってもACK、NAK、CANのいずれも戻ってこなかった場合には、送信側はNAKを受け取ったとみなして、前回送ったブロックを再度送信する。10秒というのは、実際にネットを使った場合には、短すぎることがある。パケット回線の混雑で応答が遅くなったり、センターマシンの負荷の都合で処理が間に合わない場合に、余計なタイムアウトが発生すると、必要以上にデータを送信することになりかねない。XMODEMを実装する時に、ウエイトを10秒ではなく、もっと長い時間に設定してある場合もあるのは、このようなケースを想定しているためである。

双方で問題になるのは、間違ってCANを受信した場合である。特に、受信側はブロックの先頭部分を受け取り損ねた場合には、SOHを受け取るまでデータを読み飛ばすことになるのだが、このデータ中に偶然CANが含まれている可能性が十分にある。これをもって転送の中断にしてしまうと、データエラーに対する回復力が極めて低くなってしまう。この問題に対しては根本的に解決することが不可能だが、多くのXMODEMの実装については、CANを2回連続で受け取った場合にキャンセルと解釈するようにしてこの問題を多少改善しようとしている。
------------------------------------------------------------------------
図2
XMODEMデータフロー概略

    受信側              送信側

            → NAK → (送信開始を要求)

            ← SOH ← (ブロック開始)
            ←  01 ← (ブロック番号)
            ←  FE ← (ブロック番号を反転させたもの)
            ←  .. ← (実際のデータ 128 バイト)
                 ..
            ← sum ← (チェックサム)
            → ACK →

            ← SOH ← (ブロック開始)
            ←  02 ← (ブロック番号)
            ←  FD ← (ブロック番号を反転させたもの)
            ←  .. ← (実際のデータ 128 バイト)
                 ..
            ← sum ← (チェックサム)
            → ACK →

                 ..   (上記手順を、データがなくなるまで繰り返す)

            ← EOT ← (転送終了)
            → ACK →

------------------------------------------------------------------------

2.1.2 CRC XMODEM

XMODEMと呼ばれているものにも、いくつかの改良版がある。まず、チェックサムによりデータエラーをチェックする方法は、偶然化けたデータのチェックサムが一致することもあり、信頼性がいまいちであることが問題になった。 この問題をクリアするために、チェックサムではなくCRCを用いてエラー検出するようにしたのが、CRC XMODEMである。この誤り率であれば、実用的にはエラーを検出し損なうことはまず考えなくてよい。 CRCの計算方法をリスト1に示す。

------------------------------------------------------------------------
リスト1
CRCの計算手順

unsigned short updcrc(c, unsigned crc)
{
    int count;

    for (count = 8; --count >= 0; ) {
        if (crc & 0x8000) {
            crc <<= 1;
            crc += (((c<<=1) & 0400) !=  0);
            crc ^= 0x1021;
        } else {
            crc <<= 1;
            crc += (((c<<=1) & 0400) !=  0);
        }
    }
    return crc;
}
------------------------------------------------------------------------

CRC XMODEMの場合は、XMODEMの転送開始の合図となるNAKの代わりに、'C'というキャラクタを受信側が指定する。'C'を送っても3秒以内に応答のない場合には4回まで'C'を送る。それでも応答のない場合には、送信側がCRCの機能を持っていないとみなし、NAKを送信して、以後はCRCでなくチェックサムを用いたXMODEM手順を実行する。この方法の場合には、'C'がNAKに化けた場合、あるいはその逆の場合にトラブルの原因となることが考えられるが、現実的には極めて希なケースであるため、このレベルでの実用的にはひとまず耐えられると考えるのが妥当だろう。

------------------------------------------------------------------------
図3
受信側がCRCを指定し、送信側にその機能がない場合

    受信側              送信側

             →  C  →  (CRC XMODEM を指定)
                        (3秒でタイムアウト)
             →  C  →  (CRC XMODEM を指定)
                        (3秒でタイムアウト)
             →  C  →  (CRC XMODEM を指定)
                        (3秒でタイムアウト)
             →  C  →  (CRC XMODEM を指定)
                        (3秒でタイムアウト)
             → NAK →  (チェックサム XMOMEM を指定)
             ← SOH ←  (ブロック開始)
             ←  01 ←  (ブロック番号)
             ←  FE ←  (ブロック番号を反転させたもの)
             ←  .. ←  (実際のデータ 128 バイト)
                 ..
             ← sum ←  (チェックサム)
             → ACK →

                 ..     (上記手順を、データがなくなるまで繰り返す)

             ← EOT ← (転送終了)
             → ACK →
------------------------------------------------------------------------

2.1.3 XMODEM-1k

パケット回線を経由してデータ通信を行う場合は、ブロックが長い方がパケットの長さに対する余り部分のタイムアウトが少なくなり、さらに、アクノリッジを戻す回数も減るため、総合的に転送速度を向上することができる。XMODEMのブロックサイズを1024バイトにしたものが、XMODEM-1kである。これは従来のXMODEMと手順は全く同じで、ロングパケットに対する処理が追加されているだけである。 パケットの長さは128バイトの時のSOHの代わりにSTXが用いられている時に1024バイトと解釈する。フロー概略は図4の通りである。実際は、128バイトブロックと1024バイトブロックの混在が可能である。受信側は、SOHとSTXを見分けて処理すれば、両者を区別できるからだ。特に、データの長さがブロックサイズで割り切れないような場合、最後のブロックの長さを合わせるために、残り部分をパディングして長さを合わせなければならないが、この時に残りデータが128バイト以下であれば、128バイトブロックを利用した方が合理的である。

XMODEM-1kのエラーチェックはCRCである。パケットが長くなることにより、チェックサムの検査では、特定のビットが化けるようなエラーへの回復力が期待できなくなるためである。

------------------------------------------------------------------------
図4
XMODEM-1kデータフロー概略

    受信側              送信側

             → 'C' → (チェックサムによる送信指定)

             ← STX ← (ブロック開始)
             ←  01 ← (ブロック番号)
             ←  FE ← (ブロック番号を反転させたもの)
             ←  .. ← (実際のデータ 1024 bytes)
                 ..
             ← CRC ← (CRCの上位バイト)
             ← CRC ← (CRCの下位バイト)
             → ACK →

             ← STX ← (ブロック開始)
             ←  02 ← (ブロック番号)
             ←  FD ← (ブロック番号を反転させたもの)
             ←  .. ← (実際のデータ 1024 bytes)
                 ..
             ← CRC ← (CRCの上位バイト)
             ← CRC ← (CRCの下位バイト)
             → ACK →

                ..     (上記手順を、データがなくなるまで繰り返す)

             ← EOT ← (転送終了)
             → ACK →
------------------------------------------------------------------------

2.2 YMODEM

2.2.1 YMODEM 誕生の背景

XMODEMはBBSの転送プロトコルとして標準的な位置を占めると急速に普及したが、その機能があまりにも単純であるため、改善の余地をいくつか残す結果となった。特に大きな問題は、ある種の環境下では速度が著しく低下してしまうことだった。

通信回線の中には、パケット回線を経由する場合がある。大量のデータをやりとりするためには、パケット化は効率的な技術であるが、反面、リアルタイムのレスポンスに関しては弱点を持つことになる。パケットの基本的な発想は、ある程度データがまとまってから送信することにある。例えば、256バイトのデータ単位でパケットを処理する場合を考えてみよう。XMODEMのプロトコルは、128+αバイトのブロックでデータを送る。各ブロックを送信した所で、データが正しく伝わったかどうかの応答を待ち、OKなら次のブロックを送る。NAKが戻ってきたら、今送ったパケットを再度送信することになる。 ここで注目して欲しいのは、128+αバイトという長さが256より小さいということである。このような時には、パケットは原則として256バイトのデータを受け取ってから一気に送信することになるから、ただちには送信されない。しかし、256バイト受け取るまで待っていては、いつまでたっても送信することができない。 これでは都合が悪いので、パケットの処理にはタイムアウトという概念が導入されている。一定の時間待っても次のデータが来ないなら、受け取ったデータがパケットのサイズに至らなくても送信するのである。 このデータが送信されるには、タイムアウトの時間が経過することが必要であり、これがデータの転送速度低下の原因となる。

XMODEMでは、さらに悪いことに、応答は1バイトのACKまたはNAKにより確認するのであるが、これは明らかにパケットサイズよりも小さいから、やはりタイムアウトが発生するまで応答が伝わらない。このように、データを送信する時と、その応答を受け取る時の2度にわたり、タイムラグが発生することになる。 その結果、XMODEMをパケット回線経由で利用した場合には、転送速度が理想的な値の半分以下になってしまうことが少なくない。

パソコンとパソコンを直接接続する場合には、タイムアウトが発生しない仕組みになっていれば、このような速度低下はあまり考えなくてもよい。ところが、モデムと電話回線を使った場合に、同様の問題が発生する。最近のモデムのほぼ全機種には、エラー訂正機能や、データ圧縮機能が備わっている。 この機能を実現するために、モデムとモデムの間のデータ転送は、モデムの機能としてパケット化を行なっている。従って、パケット回線経由で発生したのと同じ理由のタイムラグが発生することになる。 ネットを使っている人なら、「XMODEMを使う場合にはMNPを無効にした方がいい」というアドバイスを見たことのある人は多いと思う。これは、MNPを無効にすれば、モデムがパケット化の処理を行なわなくなるために、タイムラグを避ける効果があるからである。 その代わり、データエラーの危険が生じることになるが、一応XMODEM自体にデータエラーのチェック機能があるので、こちらはあまり問題にはならない。 俗に、MNPとXMODEMの相性が悪いのは有名であるが、その理由として「MNPでエラーチェックを行い、XMODEMでさらにエラーチェックを行なう作業に時間がかかるため」と説明されることがある。この論理は怪しい。 実際、最近のパソコンのパワーから考えれば、そのような作業による遅れは殆どないと考えてよいと思う。 おそらく、大部分のパソコンで通信データを受け取る場合には、割り込みによって処理を行なうはずである。もしエラーチェックの処理が、割り込み処理の合間に完全に終了する程度の負荷だったら、エラーチェックの負荷は通信速度を低下させる原因には殆どなり得ないのである。そして、現在使われているパソコンでは、非常に早い速度で通信しない限りは、全くそうなのである。

※ただし、パソコンがいくら強力だとは言っても、WINDOWS NTのようなマルチタスクの環境になってくると、話はややこしくなるだろう。また、シリアルポート以外のI/O処理による負荷が多い場合には、割り込み処理が追い付かないという場合も考えられる。

XMODEM のもう一つの弱点は、一度に一つのファイルの内容しか送れないということである。 ファイル名や、その他の情報は送る機能を持っていない。それぞれのアーカイブファイルを転送すると、その中に含まれているファイルの作成時刻等の情報は保存されるが、アーカイブファイルそのものの名称や作成時刻は受け取り側には伝わらないのである。

このような弱点を克服するために、XMODEMを改良するというアプローチで開発されたのがYMODEMである。

◆ YMODEMの特徴

2.2.2 ヘッダ情報

YMODEM BATCH は、最初のブロックを0番とし、これをヘッダ情報と呼ぶ。 ここにはファイル名およびオプションとして、ファイルに関するその他の情報を格納する。 このブロック番号は1ではなく0であることに注意が必要である。ファイル名はNULで終了するアスキー文字列である。おそらく、殆どの場合がファイル名は128バイト未満であるが、この場合にはブロックの余り部分は0x1aではなく、NULで埋めなければならない。 ファイル名の情報は、通常はディレクトリ情報を含まずに、純粋にファイル名の部分のみを用いる。MSDOSで使われているドライブ情報(A:、B:など)も送らない。 大文字と小文字を区別しないOSからファイル名を送信する場合は、小文字に変換して送信するのがよい。例えばこれはMSDOSからUNIXへファイル転送する場合がこれに該当する。また、受信側は受け取ったファイル名を、OSの条件に合わせて適当に解釈すべきである。ディレクトリのパス区切りを表現する場合にはスラッシュ(/)を用いる。MSDOSでは円マーク(\)がディレクトリ区切りになっているが、これはスラッシュに変換すべきである。

オプションとして、ファイルのサイズを送信することができる。これにより、最後のブロックのパッド部分を、受信側が正しく判断することができる。すなわち、最後のブロックの処理においては、ファイルのサイズを越えたデータはパディングとみなし、作成するファイルに加えない。ファイルサイズはアスキー文字列によって表現する。終端のNULは不要である。

さらに、オプションとしてファイルの変更日時を送信することができる。MSDOSではこれはファイルの作成日時と同じ意味を持つ。時刻の表現は、世界標準時(UCT)1970年1月1日からの秒数を、アスキー文字列によって表現したものである。 ファイルの変更日時は、ファイルサイズの後に空白(0x20)を1文字入れることにより区別できるようにする。

さらに、オプションとしてファイルのモードを送信することができる。例えばリードオンリーの属性のファイルであることを、このオプションで伝えるのである。ファイルモードは、8進の表現を用いたアスキー文字列で表現する。これは、おそらくUNIXユーザーにはおなじみの表現である。ファイルモードは、変更日時の後に空白(0x20)を1文字入れることにより区別できるようにする。

この後に、オプションとしてファイル転送プログラムの持つシリアルナンバーを送信することができる。これもファイルモードと空白で区切って表現する。

------------------------------------------------------------------------
図5
YMODEM BATCH ヘッダ情報

           -rw-r--r--  6347 Jun 17 1984 20:34 bbcsched.txt

           00 0100FF62 62637363 6865642E 74787400   |...bbcsched.txt.|
           10 36333437 20333331 34373432 35313320   |6347 3314742513 |
           20 31303036 34340000 00000000 00000000   |100644..........|
           30 00000000 00000000 00000000 00000000
           40 00000000 00000000 00000000 00000000
           50 00000000 00000000 00000000 00000000
           60 00000000 00000000 00000000 00000000
           70 00000000 00000000 00000000 00000000
           80 000000CA 56

(X/YMODEM Protocol Reference Figure 5. より引用)
------------------------------------------------------------------------

2.2.3 転送手順

受信側は、ファイル名ブロックが正しく受け取ると、ACKを返送する。正しく受け取れない場合には、NAKを返送し、送信側はこれを受けた場合には、もう一度ブロック0を送信する。

ファイル名が送信された後のデータ転送手順はXMODEMと同様である。

一つのファイルの転送が終了したら、受信側は再び 'C' を送信し、送信側に次のファイル転送を催促する。これを、全てのファイルが転送されるまで繰り返すのである。全てのファイルが転送されたら、送信側は最後に、ブロックのデータ内容が全てNULで埋められたヘッダ情報を送信する。受信側はこれをもって一連のデータ転送終了とみなし、ACKを戻して、全処理を終了する。

------------------------------------------------------------------------
図6
YMODEM BATCHデータフロー概略

    受信側              送信側

             → 'C' → (CRCによるファイル転送の催促)

             ← SOH ← (ブロック開始)
             ←  00 ← (ブロック番号)
             ←  FF ← (ブロック番号を反転させたもの)
             ←  .. ← (ファイル名)
                 ..
             ← CRC ← (CRCの上位バイト)
             ← CRC ← (CRCの下位バイト)
             → ACK →
             → 'C' →

             ← STX ← (ブロック開始)
             ←  01 ← (ブロック番号)
             ←  FE ← (ブロック番号を反転させたもの)
             ←  .. ← (実際のデータ 1024 bytes)
                 ..
             ← CRC ← (CRCの上位バイト)
             ← CRC ← (CRCの下位バイト)
             → ACK →

                 ..    (上記手順を、データがなくなるまで繰り返す)

             ← EOT ← (転送終了)
             → NAK → (確認のため、故意にNAKを戻す)
             ← EOT ← (転送終了)
             → ACK →

             → 'C' → (次のファイルを催促)

                 ..     (一つめのファイル手順と同様)

             → 'C' → (次のファイルを催促)

             ← SOH ← (ブロック開始)
             ←  00 ← (ブロック番号)
             ←  FF ← (ブロック番号を反転させたもの)
             ←  00 ← (全てNUL)
                 ..
             ← CRC ← (CRCの上位バイト)
             ← CRC ← (CRCの下位バイト)

             → ACK → (終了)
------------------------------------------------------------------------

2.2.4 YMODEM-g

YMODEMによって、ファイル名の情報などをプロトコル内部で処理することが実現された。しかし、1024バイトのデータ単位でブロックを扱うことができるとはいえ、XMODEMと処理は本質的に変わっていない。特に、ブロック毎にポジティブアクノリッジが必要である以上、待ち時間によるロスは避けられない。YMODEMが開発されてからもハードウェアの進歩は急激であり、特に高速かつエラーフリーのモデムを安い価格で入手できるようになると、このロスがかなりの問題になってきた。

YMODEM-gは、エラーフリーの環境でのみ使うことのできる、苦肉の策である。受信側は、'C'ではなく'G'を指定してセッションを開始する。図7を見ていただければわかるように、送信側はブロックに対するACKを一切待たずに、続けてどんどん情報を送り出す。受信側はこれをそのまま格納する。もちろん、エラーチェックは行う。エラーが発生した場合はどうするか。YMODEM-gには、それを回復する手順はない。そこで転送を中止するしかないのである。しかし、エラーフリーの環境という前提の元では、エラーは発生しないのである。

ACKを待つ処理が省略されているため、これ以前のプロトコルでは避けられなかった、ACKを待つ時間のロスはYMODEM-gにはない。圧縮などを用いない条件で殆ど理論的な最高のパフォーマンスが期待できる。ただし、この場合に考慮しなければならないのは、フロー制御である。エラーフリーのモデムの場合には、ハードウェアでフロー制御を行う機能が付いているのが普通なので、これを使って正しくフロー制御を行い、バッファが溢れたりしないように注意しなければならない。

------------------------------------------------------------------------
図7
YMODEM-g データフロー概略

    受信側              送信側

             → 'G' →  (CRCによるファイル転送の催促)

             ← SOH ←  (ブロック開始)
             ←  00 ←  (ブロック番号)
             ←  FF ←  (ブロック番号を反転させたもの)
             ←  .. ←  (ファイル名)
                 ..
             ← CRC ←  (CRCの上位バイト)
             ← CRC ←  (CRCの下位バイト)
             → ACK →
             → 'C' →

             ← STX ←  (ブロック開始)
             ←  01 ←  (ブロック番号)
             ←  FE ←  (ブロック番号を反転させたもの)
             ←  .. ←  (実際のデータ 1024 bytes)
                 ..
             ← CRC ←  (CRCの上位バイト)
             ← CRC ←  (CRCの下位バイト)

                 ..     (ACKを待たずに最後まで全部転送する)

             ← EOT ←  (転送終了)
             → ACK →

             →  G  →  (次のファイルを催促)
                        (以下はYMODEM BATCHと同じ)

------------------------------------------------------------------------

2.3 ZMODEM

2.3.1 背景

ZMODEMは、名前からもわかる通りXMODEM、YMODEMを経て発展してきたプロトコルである。

X/YMODEMには、いくつかの欠点があった。特に大きな問題は、パケットサイズが固定であるため、最後にパディングが必要であること。これはYMODEMの場合はデータサイズを送信することにより、余計なデータを捨てることも可能なので、その意味では致命的ではない。もっと大きな問題は、パケット毎にポジティブアクノリッジを待たねばならないので、転送速度が遅くなることである。YMODEM-gのように、エラー訂正を放棄することによって速度を向上することは可能だが、文字化けのある回線では使えないという欠点がある。 もう一つの欠点として、8bitのコード全てをパスするような回線で用いなければならないことが指摘できる。XON/XOFFの制御を行う回線で、そのままX/YMODEMを使うことはできない。

ZMODEMはこれらの欠点を克服すべく開発されたのである。

2.3.2 ZMOMEMの特徴

ZMODEMは、いくつかの点で、X/YMODEMとは大きく異なった設計となっている。特に大きな違いは、スライディングウィンドウと、クオート処理の追加である。 スライディングウィンドウの採用は、パフォーマンスを著しく向上させた。もはやパケット回線経由でのデータ転送時にも、余計な遅延時間は感じさせない。 クオート処理はYMODEMにもなかった処理である。このため、YMODEMはXON/XOFF制御を行う回線では使えないという弱点があった。ZMODEMはこれらのコントロールコードをクオート処理することにより、問題を解決している。

パケットサイズを可変としたことにより、X/YMODEMのパケットの形式とは、もはや互換性を保持しない。

2.3.3 ヘッダパケット情報

ZMODEMのフレームは、ヘッダパケット情報をバイナリ形式だけでなくHEX形式で送ることができる。ヘッダ情報は、パケットタイプにより2種類に分類されるが、いずれも4バイトの情報が含まれている。

--------------------------------
図8
ヘッダパケット情報

TYPE:  パケットタイプ
F0: フラグの場合のLSB
P0: ファイル位置の場合のLSB
P3: ファイル位置の場合のMSB

	TYPE  F3 F2 F1 F0
	      --------------
	TYPE  P0 P1 P2 P3

--------------------------------

以下、パケットタイプを紹介する。これらはzmodem.hでその値が定義されている。

ZRQINIT
送信側から送る。受信側はこれを受けてZRINITパケットを送信することになる。

ZRINIT
受信側から送る。ZF0とZF1には受信側が使うことのできる機能を示すフラグ(表2)が格納される。ZP0およびZP1には、受信側のバッファサイズを格納する。ただし、ノンストップI/Oが可能であれば0を格納する。

------------------------------------------------------------------------
表2

#define CANFDX  1 /* Rx can send and receive FDX */
#define CANOVIO 2 /* ディスクI/O処理中にデータを受信可能 */
#define CANBRK  4 /* ブレーク信号送信可 */
#define CANCRY  8 /* 暗号化機能 */
------------------------------------------------------------------------

ZSINIT
送信側の機能を格納する。このヘッダの後にはバイナリデータパケットが続く。このパケットは、Attn文字列が格納される。

ZACK
ZSINIT、ZCHALLENGE、ZCRCWへのアクノリッジである。ZP0〜ZP3はファイルのオフセットである。ZCHALLENGEへのアクノリッジの場合は、受け取ったのと同じ情報を格納して返信する。

ZFILE
ファイル送信開始を意味するヘッダである。ZF0〜ZF2にはオプションを格納することができる。

ZF0はデータ変換を指定するオプションである。ZCNLが指定されると、行末のコードを環境に応じて変換する。通常、行末はCRとLFの組か、あるいは片方が用いられるのだが、環境によってこれがまちまちなので、テキストデータの場合には適当に変換することが必要である。ZCRECOVは、途中まで転送されたデータが存在する場合に、継続して転送を行うことができることを表す。

ZF1は、既存ファイルがある時などに、どのように扱うかを指示するオプションである。詳細は表3を参照。

ZF2は転送オプションである。ZTLZWが指定された場合の圧縮アルゴリズムはcompress 4.0をVAX上で実行した時のバイトオーダーに等しい。圧縮サイズは12ビットを指定する。

表3 ZFILEオプション
ZF0:データ変換オプション

ZCBINバイナリ転送(変換は行わない)

ZCNL行末の変換を行う

ZCRECOVファイルリカバリを行う
ZF1:マネージメントオプション

ZMNEWファイルが新しいか長さが異なる場合のみ処理する

ZMCRCファイルの長さかCRCが異なる場合のみ処理する

ZMAPND既存ファイルがあれば、データは追加される

ZMCLOB既存ファイルがあれば、データは上書される

ZTSPARSsparse fileとして扱う(特殊なデータ形式)
ZF2:Transport Option

ZTLZWLempel-Ziv圧縮

ZTCRYPT暗号化

ZTRLERun Length圧縮

ZSKIP
受信側が、ZFILEに対して該当ファイルの処理を飛ばすことを指示する。

ZNAK
ネガティブアクノリッジ。

ZABORT
受信側から、ファイル転送の中断を要求する時に送信する。これを受けた送信側は、ZFINの処理に移る。

ZFIN
送信側からZMODEMのセッションを終了する時に送信する。受信側は、これを受けると同じくZFINを送り返す。

ZRPOS
受信側から、ファイルの転送オフセットを指示する場合に送信する。オフセットはZP0〜ZP3に格納する。

ZDATA
ZP〜ZP3にファイルオフセットを格納し、その後にデータパケットが続く。

ZEOF
送信側から、ファイル終了を通知する。ZP0〜ZP3はファイルの最後のオフセットを意味する。

ZFERR
ファイル入出力時にエラーが発生したことを意味する。ZABORTと同様に解釈して処理を行う。

ZCRC
受信側から送る場合は、ファイルのCRC要求を意味する。送信側は、ファイルのCRCを計算して、ZP0、ZP1に格納して、受信側に返信する。

ZCHALLENGE
受信側から送る。これは双方が想定したプログラムにより実行されていることを確かめるための手続きで、ZP0〜ZP3にはランダムな数値が入る。

ZCOMPL
処理完了を意味する。

ZCAN
これはgethdr関数がキャンセルを受け取った時に処理上用いられる定義であり、データとして送信されるものではない。

ZFREECNT
送信側から送る。これを受け取った受信側は、ZACKのZP0〜ZP3に、現在のファイルシステムに対する空き容量を格納して返信する。0を戻した時は特別な意味として、十分な容量があることを意味する。

ZCOMMAND
コマンドを実行する。詳細は省略。

◆ バイナリヘッダパケット --

バイナリヘッダパケットは、送信側から受信側へのみ送られる。このパケットは、ZPAD、ZDLE、ZBINのシーケンスで始まる。

このパケットはzsbhdr関数によって送信される。受信はzgethdr関数により行う。

------------------------------------------------------------------------
図9
バイナリヘッダパケット

            * * ZDLE TYPE F3/P0 F2/P1 F1/P2 F0/P3 CRC-1 CRC-2

(*はZPAD)
------------------------------------------------------------------------

◆ HEXヘッダパケット

HEXヘッダパケットは、受信側から送信側に対して送られる。また、送信側がバイナリヘッダパケットを扱うことができない場合にも用いられる。このパケットはデータをHEX形式に変換して送るため、XON/XOFFフロー制御と衝突することがない。このパケットを受信する処理で、XON/XOFFを受け取った場合に無視すればよいのである。

HEXヘッダパケットは、ZPAD, ZPAD, ZDLE, ZHEXのシーケンスで始まる。続くposition/flagバイトの4倍とは、HEX形式に変換される。HEX形式は0〜9およびa〜fの文字によって表現される。アルファベットは小文字を使わなければならない。例えばCという文字はX/YMODEMにおいて特別な意味を持つからである。CRCの後にCR, LFが続くが、これらは特に意味を持つのではなく、単にプリントした時に見易いために追加されている。XONが最後に追加されているのは、何かの問題によってXOFFの状態になってしまった時の回復のためのアイデアである。

HEXヘッダパケットはzshhdr関数によって送信される。受信はzgethdr関数により行う。

------------------------------------------------------------------------
図10
HEXヘッダパケット

       * * ZDLE TYPE F3/P0 F2/P1 F1/P2 F0/P3 CRC-1 CRC-2 CR LF XON
------------------------------------------------------------------------

◆ バイナリデータパケット --

データを格納するパケットはバイナリデータパケットと呼ばれる。このパケットは可変長のデータをサポートし、0〜1024バイトの間の任意のサイズを指定できる。データが長いとエラーが発生した時に再送する量も多くなる。経験的には、4800bpsより転送速度が遅ければ、256バイト/パケット、転送速度が速いならば1024バイト/パケットを使うのが効率的である。また、エラーフリーの状態で転送するならば、1024バイト/パケットにするのがよい。0バイトのデータパケットは、全くデータがないので無意味のようだが、タイムアウトが発生するのを防ぐために、わざと送る場合に用いられることがある。

このパケットはzsbdata関数によって送信され、zrbdata関数で受信する。

2.3.4 転送処理

双方でzmodemを処理するプログラムが起動した状態であるとする。まず、送信側は ZRQINITパケットを送信する。受信側はこれを受け取ると、ただちにZRINITパケットを送信する。送信側はZRINITパケットを受け取ると、ZMODEMの処理を開始するのだが、この時点で'C'、'G'、あるいはNAK等が戻ってきた場合には、X/YMODEMの処理に切り替える。

転送開始時の処理としては、ZCHARRENGEパケット、ZSINITパケットの処理などがオプションとして用意されているが、詳細は省略する。

送信側は、ZRINITパケットを受け取ったら、ZFILEヘッダーに続いてZCRCWデータパケットを送信する。このパケットはYMODEM Batchプロトコルで使われているのと同じ形式であり、ファイル名、ファイルサイズ、ファイル変更日時等の情報を含んでいる。受信側はこのパケットを受け取ったら、受信側の環境に合わせたファイルを作成するための準備を行い、データ受信に備える。この時、名前もサイズも同じのファイルが受信側にあれば、ZCRCパケットを送って送信側の持っているデータのCRCを問い合わせることができる。同じファイルが既に存在している場合には、ファイルを送信するのが無駄なので、スキップするための情報とするのである。スキップしたい場合には、受信側はZSKIPパケットを送信する。複数のファイルを連続して送る場合には、この手順により特定のファイルの送信を抑制することができる。

受信側は、ファイルの受信要求としてZRPOSパケットを送信側に送る。このパケットは、ファイルのどの箇所から送信を要求するかをオフセット情報として持つ。通常はファイル先頭から送信することを要求するため、オフセットは0である。オフセットが意味を持つのは、受信側が、何等かの原因でデータ受信の途中で失敗し、ファイルの途中までのデータが既に手元にある場合である。この場合にファイルの先頭から再送するのは無駄なので、手元にあるデータのサイズをオフセットとして送信側に指示することによって、まだ得ていない情報だけを転送させるのである。

ZRPOSパケットを受け取った送信側は、オフセット情報に基づき、データを順次送信する。受信側はZDATAヘッダの情報からファイル位置を得て、期待したデータかどうかを判断する。もし期待したデータが得られなかったら、ZRPOSエラーの応答を送信側に送り、適切な位置からの再送を要求する。

データパケットのデータ部分が終了すると、ZCRCGO、そしてCRCが続く。ZMODEMでは、転送中にエラーが発生しなければ、応答が発生しないので、次のデータパケットをただちに送信する。

ZCRCQデータパケットに対しては、ZACKあるいはZRPOSパケットによるアクナリッジを待つが、データ自体はただちに継続して送られ、全二重による応答を待つことになる。

ZCRCWデータパケットは、次のフレームを送信する前に応答が戻ってくるのを待つ。これは、受信側に連続してデータを受け取る機能が用意されていない場合に用いられる。例えば、ファイルi/oの途中にデータを受信できないような場合である。

送信側がデータを送信する途中でEOFになってしまった場合には、ZCRCEデータパケットを送信する。送信する予定のデータを全て送信してEOFになれば、ZEOFパケットを送信する。受信側はデータを正しく受信し、予定していたデータを全て受け取れたと判断したら、ファイルをクローズし、ZRINITパケットを送信する。もしまだ受け取っていないデータがあれば、ZRPOSにより、送信を要求するオフセットを指示する。

この手順をくり返し、複数のファイルを一度のセッションで送信することが可能である。セッションを終了する場合には、送信側はZFINパケットを送信する。受信側は、これを受けて、送信側に対して同じくZFINパケットを送信する。送信側はZFINパケットを受け取ったら、OOという文字列を送信して、この時点でただちに処理を終了する。受信側は、しばらくの間、"O"という文字が戻ってくるのを待つが、これが受け取れても受け取れなくても、処理を終了する。

------------------------------------------------------------------------
図11
ZMODEMデータフロー概略

    受信側                      送信側

            ← ZRQINIT(0) ← ZRQINIT(0)
            → ZRINIT     → ZRINIT
            ← ZFILE      ← (ファイル情報)
            → ZRPOS      → (送信開始位置)
            ← ZDATA      ← (データを送信する)
                  ..         (データを送信する)
            ← ZEOF       ← ファイル終了
            → ZRINIT     →
            ← ZFIN       ← (送信終了)
            → ZFIN       → (受信終了)
            ← OO         ←
------------------------------------------------------------------------

◆ キャンセル処理

ZMODEMのキャンセル処理は図12のような文字列によって行う。2つのZPADに続いて8個のCAN、そして10個のBSが続く。BSはZMODEM内では特に意味を持たないで読み飛ばされるのだが、どちらかがトラブルでZMODEMの処理を抜け出してコマンド入力モードになってしまった場合に、エコーバックをクリアするために付加されている。

------------------------------------------------------------------------
図12
キャンセル文字列

static char canistr[] = {
    ZPAD, ZPAD, 24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
};
------------------------------------------------------------------------

◆ ATTENTIONシーケンス

ZMODEMでは、エラーが発生しない場合には、特に指定がある場合を除いてはアクノリッジを返さない。この場合、エラーが発生した時にどのような応答シーケンスを戻すかを、あらかじめ指定することができる。この文字列はオプションのZSINITフレームにより送信される。

2.4 B Plus Protocol

2.4.1 特徴

B+プロトコルの正式名称はCompuServe B Plus Protocolである。その名の示すように、CompuServeにおいて開発されたプロトコルである。このプロトコルをさかのぼると、Quick Bプロトコル、Bプロトコル、さらにそれより前のAプロトコルにまでたどり着く。

B+プロトコルは、ネットをパソコンを使ってアクセスすることを意識した仕様になっている。このため、端末の情報をホスト側に送るような特徴的な機能も持っている。XMODEMのように、処理時にXON/XOFF制御を無効にする必要もなく、ソフトウェアで頑張れば、ユーザーの手間は少なくてすむように配慮されたプロトコルである。

Quick BというプロトコルとB+とはどこが違うのか、よく質問されるのだが、これは歴史的な理由による違いである。 B+プロトコルは、Bプロトコル、Quick Bプロトコルを経て成長してきた。Quick BはB+の暫定版として解釈するとよい。従って、内部処理のかなりの部分が共通であるが、細かい所が改善されている。差を表4に示す。B+に対応のセンターにQuick Bで接続しようとすると、自動的にB+側が判断してQuick Bのレベルに対応するため、端末側では特にどちらであるかを意識する必要はないはずである。実際は、いわゆる「ソフトウェアの相性」でうまくいかないという話も聞いたことがあるのだが。ENQに対する応答でパソコン側がどのレベルであるか判断できる筈なので、理屈では問題ないのだ。

表4
Quick B と B+ の違い

BQuick BB+
<ENQ>への応答 <DLE> 数値 <DLE> + <DLE> 数値 <DLE> + + <DLE> 数値
クオート対象
(デフォルト)
0x00〜0x1f 0x00 0x03 0x05 0x10
0x11 0x11 0x13 0x15
0x03 0x05 0x10 0x11
0x13 0x15
(拡張)使えない4種類から選択任意に指定可能
パラメータ
パケットの
クオート対象
使えない 0x00〜0x1f 0x00〜0x1f
0x80〜0x9f
<NAK>への応答 最後のパケット
を再送
<ENQ><ENQ>に対する
ACKにより判断
Quick Bと同じ

B+は、いくつかのコントロールコードを文字通り制御のために使っている。 その他のコントロールコード、例えばcontrol-Sやcontrol-QはB+自身は使わない。 従って、クオートの指定をうまく使えば、XON/XOFF制御を行う設定の回線でもB+を使うことができる。文字コード0x00〜0x1fに相当するコントロールコードの一覧は前述の表を参照のこと。B+には通信開始時に双方のクオート必要な文字セットを指定する機能がサポートされているため、機能的にはコントロールコードの任意の組み合わせをクオート対象とすることができる。もちろん、全部のコードをクオートすれば、それだけオーバーヘッドも大きくなるので、必要最低限の指定を行うことが望ましい。

2.4.2 コントロールコード

B+ プロトコルで使われるコントロールコードを以下に説明する。

ENQ
0x10。イニシエータからレスポンダに対して最初に送られてくるコード。 B+によるデータ転送を開始することを意味する。 レスポンダは、これを受け取った時点で、1ブロック=512バイト、エラーチェック=チェックサム形式、シーケンス番号=0の状態に初期化する。

レスポンダはこの初期化を完了して、データを受信できる状態になったら、<DLE>++<DLE>0の5バイトのデータをイニシエータに送信する。 この内容を分解して解釈すれば、<DLE>++の部分が、+パケットの送信要求を意味し、<DLE>0はACKを意味することになるのだが、そこまで考える必要もないだろう。

ACK
B+では、ポジティブアクノリッジは、<DLE>にシーケンス番号を続けて2バイトで表現する。 これにより、どのパケットを正常に受信したかを相手に正しく伝えることができる。 エラーが検出された場合には、当然ACKは送信せず、NAKを送ることになる。

Wait
B+では、<DLE>;(セミコロン)という2バイトが時間待ちのためのコントロールコードに割り当てられている。

データ転送が実際に始まってしまえば、このようなコードはまず必要ないのだが、 途中でインタラクティブな処理が必要になった場合には、判断までの時間がどれ位かかるかわからない。「このファイル名でいいですか」と尋ねると、ユーザーが考え込むかもしれないのである。 このような処理は、例えばリジュームダウンロードを指定したが内容が異なっている時に、既存ファイルに上書きするか、それとも別のファイル名でダウンロードするかを選択させる場合などに発生する。 ユーザーが考えている間にタイムアウトとなり転送処理が中断してしまったら笑い話になってしまう。

また、双方の都合で、処理に時間がかかる場合も考えられる。B+の場合は、resume download、resume uploadの処理を始める時に、ファイルの内容に対するCRCを計算しなければならない。大きなファイルの場合や、システムの負荷が高い場合には、この計算のための時間がかなり必要になることも考えられる。 おそらく、その時間の大部分は計算そのものではなく、ファイルアクセスにかかるのであるが。この間にタイムアウトになってしまうと、これも笑えない。

そこで、一定時間処理を行なっても、次のデータを送る見通しが立たない場合には、タイムアウトとみなされる前に、もう少し待たせるための指示を出すのである。

NAK
NAK(Negative Acknowledge) は、<NAK> (0x15) の 1 バイトである。これは、何等かのエラーが発生したことを意味する。パケット番号が異常であるとか、CRCが合致しなかったり、一定時間待っても相手から何もデータが到着しない場合(タイムアウト)に送るコードである。データ送信側が NAK を受け取った場合は、<ENQ> <ENQ> を送信し、これに対して 2 つの異なった ACK が戻ってくるのを待つ。これは双方のシーケンス番号を合致させるための処理である。

Panic
処理を中断する他に手段がない場合には、<DLE>を4個連続して送信する。これは致命的な障害を意味する。

2.4.3 パケット

B+ のパケットの構造

B+のパケットは、(Lead-in)、(Sequence)、(Type)、(Body)、(Trailer)の5つの部分から構成されている。

(Lead-in)は<DLE>'B' (0x10 0x42)という2バイトで、パケットの先頭となる。

(Sequence)は、パケットに付けられる番号で、'0'から'9'までの1バイトの文字により表現する。これは0から初めて9に至るとまた0に戻す、という手順で巡回して利用する。

(Type)は、パケットの型を表す1バイトの情報で、内容は表5の通りである。

表5 (Type) の種類
'+'転送のためのパラメータを含むパケット
'T'ファイル転送のための情報を含むパケット
'N'データを含むパケット
'F'処理失敗を意味するパケット

(Body)は、パケットの内容で、B+の場合は1024バイトまでの任意の長さの可変長データを扱うことができる。

(Trailer)は、パケットの最後に付加する情報である。可変長のデータは、(Trailer)で終了することにより実現されている。
図13 (Trailer) の種類
チェックサム形式<ETX> <Checksum>
CRC形式<ETX> <CRC-H> <CRC-L>

具体的には、図13のような形式の、(Body) の終了を判断するための情報である。 <ETX>が(Body)終了となるので、(Body)中では<ETX>が出現する場合には必ずクオートしなければならない。 (Trailer)にはエラーチェックのための情報も含んでいる。図13のように、Checksum を使う場合には、<ETX> と Checksum の合計 2 バイト、CRC を使う場合には、<ETX> と CRC 2バイトの合計3バイトからなる。

エラーチェックの対象は、(Sequence) (Type) (Body)、そして <ETX> も含む。 これらのうち、(Body) と <Check Value> の内容がクオートされている場合は、エラーチェックにはクオートされる前の値を用いる。 <DLE> は含まれない。

クオートの方法は、対象のデータが 0x00 - 0x1f の場合には、0x40 を加えた値を <DLE> の後に続ける。0x80 - 0x9f の場合には、0x20 を引いた値を <DLE> に続ける。

+ packet

イニシエータはレスポンダとファイル転送に関する様々なオプションを合わせてからデータを転送しなければならない。このためにお互いの通信条件を確認するための情報を格納したパケットが+ packetである。+ packetのデータは、コントロール文字(0x00〜0x1f、0x80〜0x9f)に該当するキャラクタをすべてクオートした状態で扱う。B+は、クオートするコントロール文字を任意に設定する機能を持っているので、初期状態では相手がどの文字をクオートすればよいかわからない。そこで、+ packet においては、確実に大丈夫なように、全コントロール文字をクオートすることになっている。

このパケットのエラー確認方法はチェックサムで行う。

データは17 バイトの長さであり、詳細を図14に示す。将来、CompuServe が仕様を追加する場合も想定されるので、+ packet の長さがこれより長い場合には、残りを読み飛ばして処理するようにプログラミングすることが推奨されている。

この中で、Q1-Q8により、任意のコントロール文字セットをクオートすることができるのだが、DQというパラメータも残っている。これは歴史的な理由による。

------------------------------------------------------------------------
図14
+ packet

	WS WR BS CM DQ TL Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 DR UR FI

WS (Window Send)
自分が相手に対して、パケットを先送りする能力があるかどうかを知らせる。
0x00一度に 1 つずつのパケットを送信する (デフォルト)
0x01一度に 2 つまでのパケットを送信する

WR (Window Receive)
自分が、パケットをいくつまで先送りによって送られても受け取れるかを知らせる。
0x00一度に 1 つずつのパケットが受信できる (default)
0x01一度に 2 つまでのパケットが受信できる

BS (Block Size)
パケットの最大値。この値を 128 倍したバイトまでを許す。default は 512 バイト。

CM (Check Method)
エラーチェックの方法。
0x00チェックサム (default)
0x01XMODEM CRC-16

DQ (Data Quoting Level)
クオートする文字の集合を表わす。Q1-Q8が指定されている場合はDQは無視する。
DQクオートされる文字
0x000x00 0x03 0x05 0x10 0x11 0x13 0x15
0x010x03 0x05 0x10 0x11 0x13 0x15
0x020x03 0x05 0x10 0x11 0x13 0x15 0x91 0x93
0x030x00..0x1f, 0x80..0x9f

TL (Transport Layer)
0x00(default)
0x01アプリケーションはB+をトランスポートレイヤーとして扱う。

Q1-Q8
Q1-Q4は0x00-0x1f、Q5-Q8は0x80-0x9fのコントロールコードに対応するクオート文字マスクを意味する。例えば、CompuServe Hosts の場合、0x14 0x00 0xd4 0x00 0x00 0x00 0x00 0x00 となる。それぞれのビットが1の場合に、以下の表に対応するコントロール文字をクオートすることを意味する。
Bit76543210
Q10x000x010x020x03 0x040x050x060x07
Q20x080x090x0a0x0b 0x0c0x0d0x0e0x0f
Q30x100x110x120x13 0x140x150x160x17
Q40x180x190x1a0x1b 0x1c0x1d0x1e0x1f
Q50x800x810x820x83 0x840x850x860x87
Q60x880x890x8a0x8b 0x8c0x8d0x8e0x8f
Q70x900x910x920x93 0x940x950x960x97
Q80x980x990x9a0x9b 0x9c0x9d0x9e0x9f

DR (Download Resume)
ダウンロードリジューム機能の有無。
0x00サポートしない
0x01'Tr' パケットのみサポート
0x02'Tr' パケットおよび 'Tf' パケットをサポート

UR (Upload Resume)
アップロードリジューム機能の有無。
0x00サポートしない
0x01'Tu' パケットのみサポート
0x02'Tu' パケットおよび 'Tf' パケットをサポート

FI (File Information)
ファイル情報のためのパケットをサポートするかどうかを識別する。
0x00サポートしない
0x01'TI' パケットをサポート
------------------------------------------------------------------------

T packet

T packetはファイル転送に必要な、主にファイルの情報をやりとりするためのパケットである。データの転送方向を表す<Direction>、データの型を表す<Data type>、ファイル名を表す<File name> の、3つの要素からなり、詳細を表6に示す。<Data type> 'A' は、仕様書には7-bit ASCIIデータと書かれているのだが、NIFTY-Serveの場合は<data type> 'A' の状態で8-bitのデータを送ってくることに注意。

ファイル名の終了は、パケットの終了で識別するので、'\0'で終わらせる必要はない。従って、このパケットの<Body>の長さは、<Direction>、<Data type>それぞれ1バイトずつとファイル名の長さということになる。

表6
T packet
<Direction>
'D'Download (Initiator -> Responder)
'U'Upload (Responder -> Initiator)
'C'Close
<Data type>
'A'7-bit ASCII data
'B'8-bit Binary data.
'I'Image data

------------------------------------------------------------------------
以下は、エラーチェックとして CRC を指定された場合の例

	0x10 0x42 0x37 0x54 0x44 0x41 0x53 0x2e 0x43 0x03 0x57 0xff
	<DLE>  B    7    T    D    A    S    .    C  <ETX>  57   FF
	(Lead-in)   |    |    |    |   (File name)   (Trailer)
	(Sequence) -+    |    |    |
	(Type = T) ------+    |    |
			      |    |
	(Direction) ----------+    |
	(Data type) ---------------+
------------------------------------------------------------------------

N packet

N packet は転送すべきデータそのものを入れるパケットである。 事前にTパケットで送られた情報に基づき、受信側はN packetのデータを格納したファイルを作成する。B+は可変長のデータを扱うことができるので、XMODEMのようにデータの最後が余計な文字で埋められるようなことはない。

2400bps以上の速度でデータ転送をする場合には、通常は1024バイトのデータを1パケットに格納するのが効率的である。

B+のパケットは可変長である。2400bpsの場合、基本的には1024バイトのデータをパケットとするのが効率的であるが、万一データエラーの多い状態の回線に遭遇した場合には、1024バイトは長すぎるかもしれない。このような場合には、あまりリトライの頻度が多いようであれば、ある程度自動的にパケットの長さを減らしてみて、状況が改善されるかどうかを判断すべきである。

F packet

F packetは、処理中に何らかの障害があった場合に送信されるパケットである。このパケットのシーケンス番号は無視され、障害があったことのみを認識する。

<Body> の最初の 1 バイトが、状況を識別するためのコードである。残りは単に状況を報告するために使われる文字列であり、画面に表示するために使われる。

F packetを受信した側は、必ず ACK を返信し、処理を中断する。パケットを送った側は、ACK が戻ってくることを確認する。Failure パケットを送信した側は、その後に受け取ったパケットの内容を全て無視する。
表7 F Packet の意味
先頭文字
Aユーザーから処理中断要求があった(Abort)
Cメモリまたはディスクが不足した(Capacity failure)
Eその他のエラー(Processing error)
I入出力エラー(I/O error)
Mアップロードするためのファイル情報が送られていない。(Missing file request)
N処理できない Packet Type を受け取った
Sシーケンス番号異常(Sequence Number failure)
rリジュームできない(Resume failure)

Tr Packets

双方が Download Resume の機能をサポートしていて、かつ'TD' パケットを受け取った側が既存ファイルを持っている場合、レスポンダは、'Tr' パケットを用いて既存ファイルのサイズとCRCを送信する。テキスト形式のデータをダウンロードした場合には、受信側でコード変換、行末の変換を行うと、サイズ、CRCが変化することになるので、注意が必要である。

<Length> <CRC> が一致すれば、通常にファイル転送を続行し、最後に TC パケットを置くって処理を終了する。

<CRC> があわないか、またはイニシエータのファイルが <Length> よりも短い場合には、イニシエータは次のパケットを戻す。

DR = 1 の場合は処理を中断する。この場合、Fr (Failed Resume) パケットが送られるので、双方は処理を中断する。

DR = 2 の場合は、ファイルを最初から送り直す。これは、Initiator で CRC エラーが発生した場合である。この場合、Tf (CRC Failed) パケットが送られる。Responder は、了解なら ACK を送信する。

図15
Tr パケット

	<DLE> B <Sequence> T r <Length> <CRC-16> <ETX> <Trailer>

<Length> は既に受け取っているファイルのサイズを ASCII 表示による十進数で表わす。この直後には一つ以上の空白があること。

<CRC-16> は、既に受け取っているファイルの CRC を ASCII 表示による十進数で表わす。この直後には一つ以上の空白があること。

------------------------------------------------------------------------
図16
Download Resume の例

	イニシエータ		レスポンダ
		TD	→			Download要求
			←	Tr		既存データ情報の通知
		N	→			続きのデータを送信
		(以下、通常と同様)
		TC	→			送信終了


	イニシエータ		レスポンダ
		TD	→			Download要求
			←	Tr		既存データ情報の通知
		Fr	→			失敗した
		(以下、Failure Packet の手順に従い処理中止)


	イニシエータ		レスポンダ
		TD	→			Download要求
			←	Tr		既存データ情報の通知
		Tf	→			データが一致しないことを通知
			←	ACK		
		N	→			最初からデータを送信
		(以下、通常と同様)
		TC	→			送信終了
------------------------------------------------------------------------

Tu Packet

双方が Upload Resume の機能をサポートしている場合、既存のファイルが Initiator 側にあれば、イニシエータは残りのデータだけを受け取るのが効率的である。データの途中からアップロードを再開するための情報は、図17のような構造を持つTuパケットによって、イニシエータからレスポンダに対して送信される。レスポンダは既に受け取ったと報告のあったデータに対して、CRCを計算して、一致することを確認する。一致した場合には、残りのデータを送信し始める。 CRCが一致しなかった場合が問題であるが、Upload Resume機能のサポートレベルによって処理が異なる。このレベルは+パケットのURによって双方のうち小さいものに合わせられる。URが1の場合には、レスポンダはFrパケット(Failed Resume)を送信する。これはレジューム処理の失敗を意味し、処理はここで中断されることになる。 URが2の場合は、レスポンダはTfパケットを送信する。このパケットは、続行して送るデータがないことを意味するから、イニシエータは新たにファイルを作成する準備をして、先頭からデータがやってくるのを待つことになる。

------------------------------------------------------------------------
図17
Tuパケットの構造

 <Lead-in> <Sequence > T u <Data Type> <Length> <CRC-16> <File Name> <Trailer>

------------------------------------------------------------------------

------------------------------------------------------------------------
図18
Upload Resume の例

	イニシエータ		レスポンダ
		TU	→			Upload要求
			←	N		途中からデータ送信
		(以下、通常と同様)

	イニシエータ		レスポンダ
		TU	→			Upload要求
			←	Fr		失敗した
		(以下、Failure Packet の手順に従い処理中止)

	イニシエータ		レスポンダ
		TU	→			Upload要求
			←	Tf		先頭からの送信通知
		ACK	→
			←	N		最初からデータを送信
		(以下、通常と同様)
------------------------------------------------------------------------

Tf Packet

Tfパケットは、レジューム処理が実行できない場合に送信されるパケットで、+パケットにおけるDRが2でDownload Resumeを実行した場合、および、URが2でUpload Resumeをじっ個した場合にに発生する可能性がある。

TI packet

ファイル情報を格納したパケットである。ファイルの型、作成日時、サイズ、名前などの情報がTIパケットにより通知される。 このパケットはファイル送信側から送信する。すなわち、ダウンロードの場合にはイニシエータ、アップロードの場合にはレスポンダが送信する。 TIパケットには、データ圧縮方法を通知するパラメータが予約されている。この箇所の情報に従って、送信側がデータを圧縮して転送し、受信側がそれを復元することにより、実質的な転送時間の短縮を狙っているのであろう。ただ、現実的には、バイナリのデータの多くは圧縮機能の付いたアーカイバーを使って、あらかじめアーカイブされていることが多く、このようなデータは既に圧縮されているために、さらに圧縮してサイズを小さくすることが見込めない。また、圧縮されていないデータであったとしても、モデムの種類によっては、モデム間で独自に圧縮して速度を上げる方法が取られている場合があるため、この機能が活かされていれば、特にB+で圧縮しなくても同等のパフォーマンスは期待できるかもしれない。

OSの種類によっては、ファイル情報の全てを反映したファイルが作成できないことが考えられる。例えばMSDOSにおいては、ファイル作成日時と変更日時の区別はない。ファイル名の長さも、扱うことのできる文字も制限されている。このような些細な差異に関しては、それぞれのOSに合わせてB+を処理するプログラムをインプリメントすればよい。

このパケットは、+パケットにより双方のパラメータFIが0より大きな値である時にのみ有効である。

------------------------------------------------------------------------
図19
TI packet の構造

	<Lead-in>
	<Sequence>
	T I
	<data type>
	<compression >
	<file length> *
	<creation date> *
	<creation time> *
	<modification date> *
	<modification time> *
	<true name length>
	<true name>
	<Trailer>

	* ASCII の数字による表現。区切りは1つ以上の空白による。
------------------------------------------------------------------------

表8 TI packet のパラメータ詳細
<data type>データの種類を表す。A = ASCII, B = Binary.
<compression>データ圧縮の有無。将来のために予約されている。
0 = データ圧縮なし
<file length>送信するデータの長さ。
データが圧縮される場合には、圧縮前の値を用いる。
<zone>タイムゾーン (分)
<creation date>ファイル作成日で、yyyymmddの形式。
<creation time>ファイル作成時、00:00:00 からの秒数
<modification date>ファイル変更日、'0' の場合には無視される
<modification time>ファイル変更時、'0' の場合には無視される
<true name length><true name> の長さを1バイトで表わす
<true name>ファイル名。
ディレクトリ・デバイス等の情報は含めない。

------------------------------------------------------------------------

                     "A?55387 300 19880422 52480 0 0 ?BPLUS.DOC"
                       |       |             |       |
                      0x00    EST         7:18 PM   0x09
------------------------------------------------------------------------

2.4.4 ターミナルプログラム

ターミナルに B+ をインプリメントする場合、図20の3つの状態を相互に移るような処理を書けばよい。 起動時には Terminal の状態から始まる。 B+では、DLEがキーになるコントロールの意味を持つ。

------------------------------------------------------------------------
図20
ターミナル状態遷移

1) Terminal (ターミナル状態)

次のデータが ENQ なら、normal B プロトコルの設定にリセットした上で、DLE + + DLE 0 を送信し、1) Terminal
次のデータが DLE なら、 2) DLE_Seen
それ以外のデータは、通常のターミナルソフトのように処理する。ESC シーケンスのような場合を除いて、画面にそのまま表示すればよい。

2) DLE_Seen (DLE 受信直後)

次のデータが B なら、3) Get_First_Packet
それ以外のデータなら、何もせずに 1) Terminal

3) Get_First_Packet (パケット受信処理)

このデータはパケットである。DLE B 以後のデータを処理する。
正常な '+' パケットなら、B+ の設定に状態を変更し、1) Terminal
正常な T パケットなら、通信処理を行い、終了後に 1) Terminal
それ以外の場合には、Failure Packet N を送信して、1) Terminal
------------------------------------------------------------------------

2.4.5 アクノリッジ待ち状態

B+の処理で最もややこしいのが、パケットを受信する時の処理である。 B+の仕様書にも、Ackの処理が最も大変であると書かれている。この処理は13の状態を定義し、それぞれに対して何を受け取ったらどの状態に遷移するかをコーディングしなければならない。 実は、単にパケットを受信するのなら話は簡単なのであるが、何が処理を複雑にしているかというと、トラブルが発生した場合のふるまいなのである。例えば、予期しない番号のパケットを受け取ったり、タイムアウトが発生した場合など、この手順に従って処理するように書けば、だいたいうまく行くはずである。

------------------------------------------------------------------------
図21

1) S_Get_DLE (DLE 待ち状態)

次のデータが DLE なら、2) S_DLE_Seen
次のデータが NAK なら、12) S_Send_ENQ
次のデータが ENQ なら、11) S_Send_ACK
次のデータが ETX なら、10) S_Send_NAK
それ以外のデータなら、1) S_Get_DLE
timeout なら、12) S_Send_ENQ

2) S_DLE_Seen (DLE の次の文字待ち状態)

次のデータが <digit> なら、ACK を受信したことになる
直前に ENQ を送ったなら、
次のデータが DLE <digit> であり、
<digit> が一致した場合は、13) S_Resend_Packets
<digit> が一致しない場合には、12) S_Send_ENQ
それ以外の場合は 12) S_Send_ENQ

該当パケットが保存されていれば、解放し、処理終了

次のデータが B なら、3) S_DLE_B_Seen
次のデータが ; なら、1) S_Get_DLE
次のデータが ENQ なら、11) S_Send_ACK
それ以外のデータなら、1) S_Get_DLE
timeout なら、12) S_Send_ENQ

3) S_DLE_B_Seen (DLE B の次の文字 <Sequence> 待ち状態)

次のデータが ENQ なら、11) S_Send_ACK
それ以外のデータなら、
これを保存して、
Check value をクリアして、
4) S_Get_Data

timeout なら、10) S_Send_NAK

4) S_Get_Data (データ受け取り処理)

次のデータが ETX なら、5) S_Get_Check
次のデータが ENQ なら、11) S_Send_ACK
それ以外のデータなら、
このデータに対する Check value 処理を行い、
このデータをバッファに保存し、
4) S_Get_Data

timeout なら、10) S_Send_NAK へ

5) S_Get_Check (check value 受け取り状態)

CRC を用いるなら、6) S_Get_CRC
Checksum なら、8) S_Veify_CKS
timeout なら、10) S_Send_NAK

6) S_Get_CRC (CRC 受け取り処理)

CRC を受け取ることができたら、7) S_Verify_CRC
timeout なら、10) S_Send_NAK

7) S_Verify_CRC (CRC 照合処理)

CRC が合致したら、9) S_Verify_Packet
不一致なら、10) S_Send_NAK

8) S_Veify_CKS (Check sum 照合処理)

Checksum が合致したら、9) S_Verify_Packet
不一致なら、10) S_Send_NAK

9) S_Verify_Packet(パケット照合処理)

受け取ったパケットが、目的の Sequence と一致したら、size を戻し終了
Packet Type が F なら、size を戻し終了
受け取ったパケットが、既に処理したパケットなら、11) S_Send_ACK
上のいずれにも該当しないなら、10) S_Send_NAK

10) S_Send_NAK (NAK 送信処理)

エラーをカウントする。カウンタが限度を超えたら、failure を戻し終了
Aborting 状態でなければ、NAK を送信し、1) S_Get_DLE
上のいずれにも該当しないなら、1) S_Get_DLE

11) S_Send_ACK (ACK 送信処理)

Failure packet 送信中でなければ、ACK sequence を送信
1) S_Get_DLE

12) S_Send_ENQ (ENQ ENQ 送信処理)

ENQ を 2 個送り、1) S_Get_DLE

13) S_Resend_Packets (パケット再送)

ACK を受け取っていないパケットを全て再送し、1) S_Get_DLE
------------------------------------------------------------------------

2.4.6 先送り処理

B+は、スライディングウィンドウの機能を持っているので、双方で処理できるウィンドウサイズまでのパケットは先送りすることにより、公衆パケット回線のような環境でも高速のデータ転送を実現している。ウィンドウサイズは、+パケットにおけるWSにより双方通知して、大きい方の値を採用する。

全てのパケットを送り終わったら、先送りされたパケットに対する ACK も送られたことを確認してから終了する。

------------------------------------------------------------------------
図22
パケット送信のアルゴリズム
begin
if 先送りパケット数 > WR
then
ACK を待ち、ACK 受け取り時点で先送りパケット数を減らす
endif
次のパケットを送信する
先送りパケット数に1を加算する
end
WS == 1 の場合の例
packet 0
先送りパケット数 = 0 は 1 以下だから、packet 0 を送信
先送りパケット数に 1 を加え、ACK を待たずに処理続行
packet 1
先送りパケット数 = 1 は 1 以下だから、packet 1 を送信
先送りパケット数に 1 を加え、ACK を待たずに処理続行
packet 2
先送りパケット数 = 2 は 1 以下ではないから、ACK を待ち、受け取った時点で先送りパケット数を減算
packet 2 を送信
先送りパケット数に 1 を加え、ACK を待たずに処理続行
...
packet n
packet 2 と同様の処理に従い、packet n を送信
先送りパケット数に 1 を加え、ACK を待たずに処理続行
(flush)
先送りパケット数が 0 になるまで、ACK を待つ
------------------------------------------------------------------------

ACK 減算について考えておく。例えば packet 5、packet 6 が先送りされている場合、通常は packet 5 に対する ACK が戻ってくる。もし、packet 6 に対する ACK が戻ってきたなら、packet 5 に対する ACK を受け取りそこねたと考える。従って、packet 5、packet 6 の双方の ACK を受け取ったことにする。

NAK が送られてきた場合には、ACK を受け取っていないパケットを全て再送する。

2.5 Quick-VAN

Quick-VAN に関しては、実際に動作している所を一度も経験したことがないので、参考資料を読んだだけで概略を述べるに留める。実際にインプリメントする場合には、別途資料を入手して欲しい。

2.5.1 Quick-VANの特徴

Quick-VANの特徴を一言で述べるならば、XMODEMにスライディングウィンドウ機能を追加して、速度を向上させた、という感じの通信プロトコルである。XMODEMにはなかった機能として、ファイル情報を送信したり、復旧転送を行う機能が追加されている。復旧転送とは、過去に中断した転送を再開する機能であり、ZMODEMやB+でサポートされているものと同様と思われる。

Quick-VANのエラーリカバリーは&quot;Go Back N&quot;方式が採用されている。これは、あるパケットでエラーが検出された場合に、そのパケットまで戻ってそれ以降のパケットを再送する方式である。ドキュメントに書いてある特徴としては、以下の2点が指摘されている。

その結果、オーバーヘッドを低く抑えることができる、と書いてあるのだが、これはよく意味がわからなかった。プログラムの処理が簡単になる、ということではないかと思われる。エラーが発生した時に、それ以降のパケットを全て再送する方式の他に、エラーが発生したパケットのみ再送する方式もある。すなわち、エラーが発生したパケット以降に正常に受信できたパケットがあれば、それは再送しないという選択も考えられる。 この場合は、当然データ転送量が節約できるので、回線の通信速度が処理のネックになるような場合には、全体の処理時間の節約にも結び付く。ただし、この場合は処理が若干複雑になる。 Quick-VANの再送方式としては、Go back Nの他に、選択的再送方式という言葉がドキュメントに出てくるが、その詳細は記述されていなかったため不明である。

Quick-VANとZMODEM、B+とを比較すると、Quick-VANのみクオート処理がサポートされていないという特徴がある。すなすち、Quick-VANで扱うデータはXMODEMと同じく生のデータそのままの256種類のコードとなる。クオート処理がない分、速度の点ではメリットが生じるかもしれないが、その反面、XON/XOFFを有効にしたまま使えない等のデメリットも生じる。この特徴は、Quick-VANがXMODEMのブロック構造をそのまま継承していることに基づく一つの弱点であろう。このために、何等かの原因で双方のハンドシェイクがうまくいかなかった場合に、コントロールコードとブロックに含まれているデータが区別できない、という問題が生じる。 B+では<DLE>という特別なコントロールコードは、データにそれが含まれる場合には必ずクオートされるので区別が保証されているのだが、Quick-VANの場合、特に受信側においては、データが欠落した場合の処理が問題である。 このような事態を避けるために、Quick-VANにおいてはコントロールシーケンスの検出方法が定められており、少し工夫が必要になっている。コントロールシーケンスは以下の条件が満たされた場合のみ有効とみなす。

1)ブロックのヘッダー、またはコントロールシーケンスを検索中に、初めの1バイトが当該1バイトキャラクタであった場合。 ただし、ヘッダーまたはコントロールシーケンス検索中に、初めの1バイト以外に当該制御キャラクタが現れた場合、そこまでのコードは不正コードとして無視し、そこからまた改めて検索にかかるものとする。

これは、あるべき所以外にコントロールシーケンスのコードが現れた場合には、データを不正とみなすという扱いである。

2) 1バイト制御キャラクタを受信すると、短いキャラクタ間監視タイマ(1秒)をセットし、後続データを監視する。そして、この間に後続データが到着しなかった場合。

制御キャラクタは、その1バイトだけが独立して送信されることが期待されているので、直後に別の文字が来ないことを確認するのである。時間待ちを行うのであるから、これが転送速度に影響することも考えられるが、スライディングウィンドウが順調に処理されている間の処理であれば、この程度の待ち時間が無視できるだけの先送りを行うことによって、速度への影響を回避できるだろう。実際にエラーが発生した場合は問題があるかもしれないが、現実的にはエラーが発生した場合には別の意味で時間がかかってしまうので、あまり大きな影響はないかもしれない。 実際にテストできるならば計測してみたいところである。

2.5.2 パケットの形式

------------------------------------------------------------------------
図23
<ブロック形式>

データブロック形式
内容[SOH](Num)(Num~)DATA(ChkSum)
長さ1111281

Numシーケンス番号 (0〜0xff)
Num~0xff-シーケンス番号
DATAデータ
ChkSum1バイトのチェックサム

ファンクション・ブロック形式 内容 [SOH] 0x00 TYPE BODY ChkSum 長さ 1 1 1 128 1 TYPE:'0'(0x30) SINITブロック 転送機能関係のネゴシエーション '1'(0x31) VFILEブロック ファイル情報 '2'(0x32) VENQブロック 応答要求 それ以外はリザーブ Chksum:1バイトのチェックサム    ※[SOH]からボディ終わりまでを計算する 応答ブロック形式 内容 [STX] TYPE Num BODY ChkSum [CR] 長さ 1 1 1 0〜123 1 1 TYPE:'R' RINITブロック 転送機能関係のネゴシエーション 'P' VRPOSブロック 転送開始ブロック番号の通知 'A' VACKブロック データに対する肯定応答 'N' VNAKブロック データに対する否定応答 'T' VSTATブロック 状態通知 'S' VSKIPブロック 現時点のファイルをスキップする Num:応答シーケンスナンバー VACK/VNAK/VSTAT ブロックの場合は下位7ビットをシーケンス ナンバーと一致させ、MSDを1とした0x80〜0xffを用いる。 RINIT/VSKIP/VRPOS/ の場合は0x80 BODY:可変長データで、内容はブロックによって異なる。 ChkSum:[STX]からBODYの最後までのチェックサムで、下位7ビットが有効。 MSBは1とする。

------------------------------------------------------------------------
------------------------------------------------------------------------
図24
<ブロック・ボディ部分詳細>

SINITブロック・ボディ部分詳細

 長さ 詳細
   1    バージョン情報 '0'テストバージョン、
                       '1'バージョン1.00系列
                       '2'〜'9' リザーブ
   1    再送方式       '0' Go back N
                       '1'〜'9' リザーブ
   2    送信ウィンドウサイズ 
                       &quot;00&quot;〜&quot;99&quot; によるアスキー10進表示
   1    オプション機能数
                       '0' オプション機能使用不可
                       '1'〜'8'オプション機能数
                       '9' リザーブ
   1    オプション機能1  SKIP機能
                       '0'またはNUL※オプション機能使用不可
                       '1'ファイルのスキップ機能使用可能
                       '2'〜'9'
   122  予約           NUL※ で埋める


VFILEブロックのボディ部

 長さ 詳細
   2    ファイル通し番号(セッション中で転送するファイルの番号)
                       &quot;01&quot;〜&quot;99&quot; によるアスキー10進表示
  可変  ファイル名  ファイル名を表すNULで終了する文字列
 可変 ファイル長 '0'〜'9'のアスキー表現でNULで終了する文字列
  8    ファイル作成日 &quot;19900320&quot;のような表現
          NULで埋められている場合には受信日とする
   6    ファイル作成時間※ &quot;150748&quot;のような表現
          NULで埋められている場合には受信時間※とする
  可変 リザーブ    NULで埋める


VENQブロックのボディ部

 長さ 詳細
   1    シーケンス番号 (0x00〜0xff)
   1    機能           '0' ファイル正常終結通知
                       '2'〜'9' リザーブ
   126  リザーブ       NULで

RINITのボディ部

 長さ 詳細
   1    バージョン情報 '0'テストバージョン、
                       '1'バージョン1.00系列
                       '2'〜'9' リザーブ
   1    再送方式       '0' Go back N
                       '1' 選択的再送方式
                       '2'〜'9' リザーブ
   2    受信ウィンドウサイズ 
                       &quot;00&quot;〜&quot;99&quot; によるアスキー10進表示
   1    オプション機能数
                       '0' オプション機能使用不可
                       '1'〜'8'オプション機能数
                       '9' リザーブ
   1    オプション機能1  SKIP機能
                       '0'またはNUL※オプション機能使用不可
                       '1'ファイルのスキップ機能使用可能
                       '2'〜'9'

VRPOSブロックのボディ部

 長さ 詳細
 可変 オフセット・ブロック番号
       アスキー文字'0'〜'9'を用いた10進表現。

VSTATブロックのボディ部

 長さ 詳細
   1    状態:'0':ファイル正常終結受理
             '1'..'9' :リザーブ

VSKIPブロック(オプション機能)のボディ部

 長さ 詳細
   2    ファイル通し番号  2バイトのアスキー文字'0'〜'9'による10進表現
※ドキュメントでは[NULL]と表記されているが、本文中はnullキャラクタに該当する文字を意味するNULという表現に統一した。
※筆者は「時刻」と「時間」を使い分けることに決めているが、ここはドキュメントの表記を尊重し、そのままにしている。
------------------------------------------------------------------------

Quick-VANのデータブロックは図23のような構造であり、見掛け上、XMODEMの最も標準的なブロックと全く同じ形式となっている。データが128バイト未満の場合には、余った部分を余分なデータで埋めて送信することになる。XMODEMではこの箇所に0x1aを使うが、Quick-VANではキャラクタはOS依存となっており、ドキュメントには0x00、0x1a等と書かれている。

ファンクション・ブロックは、やはり見掛け上はXMODEMの形式を踏襲している。しかし、内容は独自の変更が行われており、通常はチェックサム等の差異が発生するため、データブロックと混同されることはない。

送信側のブロックは、BODY部分を128バイトとした固定長であり、以下のような種類がある。

SINITブロックは、送信側から受信側に送るブロックであり、送信側が使うことのできる機能を受信側に通知する。これに対し、受信側はRINITブロックにより応答を行う。双方の機能が食い違っている場合には、パラメータをそれぞれ比較し、低い側の機能にお互いの実際に利用する機能を合わせる。この処理をネゴシエーションと呼んでいる。 SINITブロックで送信される情報は、バージョン情報、再送方式、ウィンドウサイズ、オプション機能数があり、オプション機能の数に従って具体的なオプション機能のパラメータを付加する。SINITブロックにおいては、余ったデータ部分は0x00で埋めて、BODY部分の長さが128バイトになるようにする。

VFILEブロックは、送信側から受信側に送るブロックであり、送信するファイルに関する詳細情報を通知する。Quick-VANにおいては複数ファイルの転送を実現するため、ファイルに通し番号を付け、セッション中で&quot;01&quot;から順に用いる。ファイル名、ファイル長は可変長のデータであり、0x00で区切られる文字列として表現する。この他にオプションの情報として、ファイルの作成日時を送信することができる。ファイルの作成日時がない場合には、受信側で、受信時刻をもってファイルの作成日時とすることになる。Quick-VANにはファイルの変更日時を通知する機能はない。VFILEブロックも、SINITブロック同様、余ったデータ部分は0x00で埋めて、BODY部分の長さが128バイトになるようにする。

VENQブロックは、送信側から受信側に送るブロックであり、受信側の状態を通知することを要求する。このブロックで用いられるシーケンス番号は、最後に送ったデータブロックに対するシーケンス番号と同じ値を用いる。受信側は、これに対してVSTATブロックをもって応答する。

受信側のブロックは、送信側とは異なり、基本的に可変長のブロックである。形式としては、図のように、やはりXMODEMを意識したデータ構造となっている。チェックサムは下位の7ビットのみを使うので注意が必要である。

RINITブロックは、SINITブロックに対する応答である。

VRPOSブロックは、既に途中まで受け取ったデータの続きを受信したい場合に、どこから送信を始めるかを送信側に指定するためのブロックである。VRPOSブロックの値は、前回正常に受信した最後のブロック番号である。従って、今回送信されるブロックは、その次の番号のブロックからである。

VSTATブロックはVENQブロックに対する応答であり、1バイトのBODYで状態を表現する。

VSKIPブロックはオプション機能であり、同一セッション中で複数のファイルを転送する場合に、スキップしたいファイルがあれば送信する。

2.5.3 タイマ

Quick-VANでは、表9、10に示したようないくつかのタイマを規定し、タイムアウトとなるまでの秒数を明確に規定している。

------------------------------------------------------------------------
表9
受信局タイマ一覧

名称 トランスポートネゴ・タイマ (RT1)
 フェーズ		ネゴサブフェーズ1
 機能概要		<NAK>送信後、SINIT受信監視タイマ
 タイマ値		20秒
 セット条件		<NAK>送信時
 リセット条件		SINIT受信時
			タイムアウト時
			<CAN>受信時
 タイムアウト処理	<NAK>再送
			10回目は<CAN>送信で異常終了

名称 ファイル情報ネゴ・タイマ (RT2)
 フェーズ		ネゴサブフェーズ2
			スキップ・サブフェーズ(オプション)
 機能概要		RINITまたはVSKIP(オプション)送信後VFILE受信監視タイマ
 タイマ値		20秒
 セット条件		RINITまたはVSKIP(オプション)送信時
 リセット条件		VFILE受信時
			タイムアウト時
			<CAN>受信時
			<EOT>受信時
 タイムアウト処理	ネゴサブ2の場合RINITを再送
			スキップサブの場合VSIKPを再送
			同一ファイルに対し10回目は<CAN>送信で異常終了

名称 転送開始要求タイマ(RT3)
 フェーズ		データ転送フェーズ(開始モード)
 機能概要		VRPOS送信後、最初のデータブロック受信監視タイマ
 タイマ値		20秒
 セット条件		VRPOS送信時
 リセット条件		VDAT受信時(n=1以外も該当)
			<CAN>受信時
			タイムアウト時
 タイムアウト処理	VRPOSを再送
			10回目は<CAN>送信で異常終了

名称 データブロック監視タイマ(RT4)
 フェーズ		データ転送フェーズ(通常モード,再送モード)
 機能概要		VACKまたはVNAK送信後、VDAT受信監視タイマ
 タイマ値		20秒
 セット条件		VACKまたはVNAKブロック送信時
 リセット条件		VDAT受信時
			タイムアウト時
			<CAN>受信時
			VENQ受信時
 タイムアウト処理	VNAKブロック送信
			同一ブロックに対し10回目は<CAN>送信で異常終了

名称 転送終了/継続監視タイマ(RT5)
 フェーズ		転送終了/継続フェーズ
 機能概要		VSTAT送信後、<EOT>またはVFILE監視タイマ
 タイマ値		20秒
 セット条件		VSTAT送信時
 リセット条件		VFILE受信時
			タイムアウト時
			<CAN>受信時
			<EOT>受信時
 タイムアウト処理	VSTAT再送
			10回目は<CAN>送信で異常終了
------------------------------------------------------------------------
------------------------------------------------------------------------
表10
送信局タイマ一覧

名称 ハンドシェイク・タイマ(ST1)
 フェーズ		ネゴサブフェーズ1
 機能概要		送信局起動後、<NAK>受信監視タイマ
 タイマ値		120秒
 セット条件		送信局起動時
			(ホスト側では、受信局起動促進MSG送出直後)
 リセット条件		<NAK>受信時
			<CAN>受信時
			タイムアウト時
 タイムアウト処理	<CAN>を送信し異常終了

名称 トランスポートネゴ・タイマ(ST2)
 フェーズ		ネゴサブフェーズ2
 機能概要		SINIT送信後、RINIT受信監視タイマ
 タイマ値		120秒
 セット条件		SINIT送信時
 リセット条件		RINIT受信時
			<NAK>受信時
			<CAN>受信時
			タイムアウト時
 タイムアウト処理	<CAN>を送信し異常終了

名称 ファイル情報ネゴ・タイマ(ST3)
 フェーズ		ネゴサブフェーズ3,継続サブフェーズ
 機能概要		VFILE送信後、VRPOS受信監視タイマ
 タイマ値		120秒
 セット条件		VFILE送信時
 リセット条件		VRPOS受信時
			VSKIP受信時(オプション)
			タイムアウト時
			<CAN>受信時
			ネゴサブ3の場合RINIT受信時
			継続サブの場合VSTAT受信時
 タイムアウト処理	<CAN>を送信し異常終了

名称 データ転送タイマ(ST4)
 フェーズ		データ転送フェーズ
 機能概要		ウィンドウクローズ後、VACK/VNAK/VSTATブロック受信監視タイマ
 タイマ値		200秒
 セット条件		応答待ちブロック数がウィンドウサイズと等しくなった
			目的ファイルの最終ブロック送信後VENQ送信時
 リセット条件		応答待ブロックリスト内のデータブロックに対応するACK/NAKブロック受信時
			応答待ブロックリストの次のデータブロックに対応するNAKブロック受信時
			VSTAT受信時
			<CAN>受信時
			タイムアウト時
			VRPOS受信時(V(S)=0時のみ)
 タイムアウト処理	<CAN>を送信し異常終了

名称 終了監視タイマ(ST5)
 フェーズ		転送終了サブフェーズ
 機能概要		<EOT>送信後、<ACK>受信監視タイマ
 タイマ値		200秒
 セット条件		<EOT>送信時
 リセット条件		<ACK>受信時
			<CAN>受信時
			タイムアウト時
			不正コード受信時
 タイムアウト処理	<CAN>を送信し異常終了
------------------------------------------------------------------------

2.5.4 ダウンロード時の処理の流れ

ダウンロード時の処理は、ネゴシエーションフェーズ、データ転送フェーズ、終了/継続フェーズの3つのフェーズに大きく分類することができる。 このうち、ネゴシエーションフェーズは、セッション開始時に一度だけ存在する。残りの2つのフェーズは、複数ファイルの転送を行う場合にファイルの数だけ繰り返される。

ネゴシエーションフェーズは、セッション開始時に、双方の環境をお互いに確認するフェーズである。受信側は、セッション開始をXMODEMと同様、NAKを送信することにより送信側に伝える。これを受けた送信側はSINITを送信し、さらに受信側はRINITを送信する。双方は、SINITとRINITの内容を比較し、双方とも実現可能な機能を以後用いるように環境を設定する。送信側は、環境を設定した後、最初のVFILE[1]を送信する。ここまでがネゴシエーションフェーズとなる。

データ転送フェーズは、実際にファイル内容を転送するフェーズである。 まず、受信側はVRPOSを送信し、データのどの位置から送信するかを指定する。送信側は、これを受け取ったらデータを次々と送信する。 このフェーズのデータは、ネゴシエーションフェーズで得た情報に基づきスライディングウィンドウを使って送られるので、図25ではデータに対してVACKが戻るように見えるが、実際はウィンドウサイズに従いデータはVACKを待たずに次々と送られる。従って、極めて短いファイルを除けば、セッション中最も長い時間を消費するデータ転送フェーズで効率的な転送が実現されることになる。 データが最後まで送られたなら、送信側はVENQを発行する。

最後に、それぞれのファイルに対するデータ転送が終了すると、終了/継続フェーズに移行する。このフェーズは、連続してファイルを転送するか、それともこれでセッションを全て終了するかを選択し判断する。 受信側はVENQを受けると、VSTATを送信する。これを受けた送信側は、さらにファイルを送信するならば、次に送るデータに対するVFILEを送信し、受信側はVRPOSを返信して、双方データ転送フェーズに移行する。 これで転送処理を終了するならば、送信側は<EOT>を送信する。受信側は<ACK>を送信して、双方処理終了となる。

実際は、問題になるのはエラーが発生した時の異常処理である。これに関しては非常に細かい規定があり、説明が大変長くなるため、恐縮ながら仕様書を参考にして欲しい。Quick-VANにおいて、異常処理が細かくなる理由の一つは、データ中にクオート処理がないために、例えば<CAN>のようなコントロールコードがそのままデータ中に含まれる可能性があるためである。コントロールコードの判断を誤らないためには、<CAN>を受け取った時に、それが本当にコントロールの目的で送られてきたのかどうかをチェックする必要があるが、この処理が複雑になっているのである。

------------------------------------------------------------------------
図25
ダウンロード時データフロー概略

    受信側                      送信側

            ------- NAK ------> [ネゴシエーションフェーズ]
            <----- SINIT ------
            ------ RINIT ----->
            <---- VFILE[1] ----
            ------ VRPOS ----->

            <---- VDAT[n] ----- [データ転送フェーズ]
            ----- VACK[n'] --->
				(注:実際はACKを待たずに次のブロック送信)
                 ..
            <---- VENQ[m,0] --- (データが全部終わった場合)

            --- VSTAT[m',0] -->  [終了/継続フェーズ]
            <--- VFILE[2] -----
            ------ VRPOS ----->

            <---- VDAT[n] -----
            ----- VACK[n'] --->
                 ..
            <---- VENQ[m,0] ---

            --- VSTAT[m',0] -->  [終了/継続フェーズ]
            <------ EOT -------
            ------- ACK ------>

------------------------------------------------------------------------

(フィンローダ NIFTY SERVE FPROG SYSOP)

(C) 1992,1998 Phinloda, All rights reserved
無断でこのページへのリンクを貼ることを承諾します。問い合わせは不要です。
内容は予告なく変更することがあります。

[Home]