ZMODEM送信の相互運用性およびパフォーマンス
Eröffnet am: 2019-09-07 11:09
Letztes Update: 2019-09-08 20:25
Auswertung: | chapuni | Verantwortlicher: | (Keine) |
---|---|---|---|
Priorität: | 5 - Mittel | Meilenstein: | (Keine) |
Typ: | Fehler | Schweregrad: | 5 - Mittel |
Komponente: | Tera Term | Status: | Offen |
Lösung | Keine |
Einzelheiten
対向の組み込みOSに対しZMODEM受信を試みています。 Teraterm ZMODEM 送信の思わしくない挙動・実装を見出したので報告します。 主に利用しているのは 4.103 ですが trunk でも同様です。 使用レートは 115200~921600bps, 8N1 noflow です。
ウィンドウサイズ
現実装ではウィンドウサイズを超過した送信を試みるような実装になっているようです。 対向の受信バッファサイズをTeratermウィンドウサイズ(デフォ 32767)と同じにした場合、この送信は受信側でオーバーランすることがあります。 オーバーランした場合、(ZCRCQを投げているにもかかわらず)ZACKが返ってくることはまずありません。
zmodem.c:625
else if ((zv->WinSize >= 0) && (zv->Pos - zv->LastPos > zv->WinSize))
これは送信動作後に実施されていますが、チェックは zv->WinSize を拠り所とするのではなく (zv->WinSize - zv->MaxDataLen) にした方が安全ではないでしょうか?
ZDATA 時の ZCRC* 選定
現実装では、ウィンドウサイズに収まっている場合に ZCRCG, ウィンドウサイズを超過したときにZCRCQを投げるようになっています。 対向は ZCRCG に対して ZACK を返送しないのがふつうなので、以下のようなシーケンスが発生します。
- Teraterm はウィンドウサイズ超過するまで ZCRCG を投げる
- 対向はエラー検出しない限り何も返送しない。
- Teraterm はウィンドウサイズ超過したときに ZCRCQ を投げ、ZACK を待つステート(Z_SendDataDat2)に移行。
- 対向がオーバーランせずにZCRCQまで受け切ったら、ZACKをTeratermに返送。
- Teraterm は ZACK を受領したのち、次のウィンドウにおける ZDATA を送信開始。
短くまとめると、Teraterm はスライディングウィンドウ動作ではなく、ウィンドウサイズ単位の半二重通信をしてしまっています。
ホスト側が USB Serial を利用しているような場合だと、USB起因の遅延も加味され、半二重通信のレイテンシ(ZCRCQ->ZACK->ZDATA)は顕著に増大します。
ZMODEM プロトコル仕様書を斜め読みしたうえで Teraterm 現実装の問題を以下にまとめます。
- 対向が信頼のおけるものではないのに ZCRCG を多用している。ZCRCG が許容されるのは、対向が報告した ZRINIT の受信バッファサイズ==0(non-stop)のときにとどめておくべきではありませんか?
- データ送信中における ZCRCQ の扱いが ZCRCW のように扱われている。仕様書を読んだ限り、ZCRCQ を投げたあとは直ちに次のZDATAを投げてもいいと理解できますが、ZACKを待つようになっています。
改善案を以下に挙げます。
- デフォで ZCRCG を投げず、ZCRCQ を投げるようにする。
- non-stop のときは ZCRCG を利用してもいいが、ときどき ZCRCQ を投げないと、エラー時の巻き戻しロスが多大になる。
- ウィンドウサイズを超過しそうなときのみ、(ZCRCQではなく)ZCRCW を投げる。
- ZACK を待つのは、ZCRCQ 送信後ではなく、ZCRCW 送信後とする。
- ZParseHdr() において、Z_SendData 時においても ZACK を受理し、zv->LastPos を更新するようにする。
- ただし ZACK 時には LastPos の巻き戻しが発生しないようにチェックを入れる。
- 巻き戻しは ZRPOS のみが行うようにする。
- ただし ZACK 時には LastPos の巻き戻しが発生しないようにチェックを入れる。
以上の変更を 1.103 の ttpfile/zmodem.c に施したうえで試してみましたが、結果は良好です。対向のCPU時間に余裕があるケースでは ZCRCW を投げることはなくなり、対向が間に合わない場合でも ZCRCW により再送を発生させず待つことができるようになりました。
Teraterm 自体、歴史のあるものなので、互換オプションを設けるのはいかがでしょうか。
- 旧仕様: {ZCRCG, ZCRCQ} を用い、ZCRCQ は ZACK を待つ。
- 新仕様: {ZCRCQ, ZCRCW} を用いる。
- ZCRCQ は ZACK を待たないが、呼応する ZACK に対しては LastPos を更新する。
- ZCRCW は ZACK を待つ。
以上、提案します。ご検討ください。
Kommentar
解析と指摘ありがとうございます。私はZMODEMの仕様を把握しておらず、使用もしていないので助かります。
互換性オプションを提案されているということは、提案された新仕様を実装すると、従来の実装で動作していた対向のZMODEM実装では扱えないことがありそう、ということですか?
新仕様での実装を試しておられるようですが、BSDライセンスでパッチとして提供して頂くことは可能ですか?