HULFTの転送速度が速い理由を技術面から説明してみる。

# はじめに
HULFT8は転送速度がFTPより速い事があります。
速い要素は色々あるのですが、今回はそのうちの1つを技術的に解説してみようと思います。

# 転送速度が早い理由の概要
HULFT8はネットワークにデータを流す際に、経路情報から最適なサイズを取得、一度に流すデータサイズを自動調整する機能があります。
これにより、効率よく転送が行われるため転送速度が速いです。

以下公式マニュアルの「転送設定の自動最適化」が該当の機能になります。
https://www.hulft.com/help/ja-jp/HULFT-V8/COM-NEW/Content/HULFT_NEW/FuncImprved/AutoOptTrnsSettings.htm

今回は、どう効率がいいのかという技術的な内容を深掘って解説します。
※なるべく分かりやすく書くよう努力しますが、前提としてTCP/IPの基礎的な知識があると伝わりやすいかと思います。

# データ書き込みとパケット分割
まずは聞いたことはあると思いますネットワークに流れるデータの単位「パケット」について説明します。

プログラミングでネットワークのデータ処理を行う際には、ソケットと言われる技術を使います。
これにより、ファイル等と同じ感覚でネットワークとのデータの読み書きを行うことが出来ます。

ただ、ソケットに大きな単位でデータを書き込んだとしても、実はその単位でネットワークにデータが流れるわけではありません。
ソケットは書き込まれたデータをまずは内部バッファに保管します。
その後、一定のサイズに分割した後、必要な情報を付与した「パケット」と言われる単位でネットワークに流れていきます。

![IPパケットを使って通信する](https://cdn-xtech.nikkei.com/atcl/nxt/column/18/00780/052700001/01.jpg)
[引用元：日経クロステック](https://xtech.nikkei.com/atcl/nxt/column/18/00780/052700001/)

基本情報を持っている人は、OSI参照モデルやTCP/IP 4階層モデルなど聞いたことがあるのではないでしょうか？
TCP/IP通信を行う場合、このデータに「TCPヘッダ」や「IPヘッダ」、「イーサネットヘッダ」など宛先情報を付与して送付しています。

![TCP/IPプロトコルのデータ形式](https://www.itmanage.co.jp/column/tcp-ip-protocol/img/ip_packet.png)

[引用元：ITコラム｜アイティーエム株式会社](https://www.itmanage.co.jp/column/tcp-ip-protocol/)

上記の例はHTTPとなりますが、HULFTもファイルのデータにアプリケーション層にて必要なヘッダを付与して転送を行っています。
以下はHULFT転送のざっくりとした仕組みを表した図になります。
![HULFTの転送の仕組み](https://www.hulft.com/application/files/6014/8239/0947/special-column-business-systems_02_01.png)
[引用元：あきみちさんがHULFTの中の人に聞いてみた](https://www.hulft.com/special-column/business-systems/business-systems_2)

TCP/IPのコネクションを確立後、双方が転送必要な情報の交換を行います。
その後、ファイルから読み込んだデータを「電文」という単位に分割して相手先に送ります。
HULFTはこの電文毎に必要なヘッダ情報を付与し、到達確認や暗号化、改ざん防止などを行っております。

このデータ電文が、実際には複数のパケットに分割されてネットワーク上に流れていきます。
今回ポイントになるのが、このデータ送信の電文のサイズになります。

# パケットサイズの決定
前の説明で、転送データがパケット単位に分割されることはおわかり頂けたでしょうか？

このパケットのサイズですが、実は固定値ではありません。
送信側受信側だけでなく、データ転送を経由する機器それぞれが扱うことのできる最大データ長を持っております。
それをMTU(Maximum Transmission Unit)と呼びます。

例えばイーサネットのMTUは1500バイトで、光ファイバ（FDDI）は4352バイトとの事です。

もし通信経路に送信元より小さなMTUの機器がある場合、データを更に分割して転送を通過させる必要があります。
再分割が進むほどネットワークの転送効率が落ちていきます。

これを防ぐために、Path MTU discoveryという経路上のMTUを調査し、配信元が予め最適なMTUの単位でパケットを分割しておく機能があります。
この単位は通信経路毎に異なるため、毎回通信のために取得し、サイズが決定されます。

![Path MTU Discovery](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144307/433e179c-2b48-024f-4057-22bfa977870b.gif)
[引用元：ネットワークエンジニアとして](https://www.infraexpert.com/info/5.2adsl.htm)


このMTUのサイズには、前に書いたTCP/IPヘッダのサイズが含まれています。
MTUからTCP/IPヘッダを除いた、純粋なデータサイズをMSS(Maximum Segment Size)といいます。

# パケット分割例
書き込まれたデータはMTU/MSSの値でパケット単位に分割・管理されるため、書き込みサイズによっては効率の悪い分割がされることがあります。
以下に実際に効率の悪いデータ分割の例を説明します。

Path MTU discoveryの結果、最適なMSSが1480だと判明したとします。

ソケットに書き込まれるデータが1481バイトだとすると……

1480バイト＋1バイト
とデータが上記２パケットに分かれて送信されることになります。

では次に、1481バイトのデータを2回書き込んだとするとどうなるでしょうか？

1480バイト＋1480バイト＋2バイト
になるとは限りません。

1480バイト＋1バイト＋1480バイト＋1バイト
と、上記4パケットに分かれて送信されることもあります。

ソケットに書き込んだデータは、直接ネットワークに出るのではなくソケットバッファに格納されます。
このバッファに書かれたデータは、OS側でヘッダを付けて送信する処理が実行されるためアプリ側から送信タイミングは制御できません。
データを分割して書き込む際は、上記のように分割されてネットワークに出ることもあります。

この様に、ネットワークに書き込むデータのサイズがMTU/MSSの倍数と一致しない場合は転送効率が悪くなる場合があります。

# HULFTのデータ送信
では、HULFTではどの様にデータを送っているかを説明します。

HULFTは元々データ送信電文のサイズを手動で設定する項目がありました。
ただ、これはユーザが値を個別に設定する必要があり、通信相手とのMSSを測定し最適値を出すのが大変という問題がありました。

そこで、HULFT8では上記で記載したPath MTU discoveryの結果をネットワークから取得し、MSS×2の倍数で転送サイズを自動調整する機能を実装しました。
これにより、お客様が設定をしなくとも無駄なデータパケットが発生せず効率よく転送できるようになりました。
なお、なぜ×2しているのかについては次の項で説明します。

# Nagle アルゴリズムと遅延 ACK
上記の理由から、転送データが大きい場合や、遠方との転送では効率よく転送されることで転送速度に影響がでてきます。

これ以外にも、転送がとある条件を満たすと遅延してしまう有名な問題があります。
その要因となる2つの機能の概要を以下に記載します。

* Nagle アルゴリズム

```
下の条件がどれか1つでも成立するまで送信を遅延させる。

・未送信データが最大セグメントサイズ以上になる
・過去の送信パケットで ACK が未受信の物がなくなる（TCP遅延ACKに注意）
・タイムアウトになる
```
[引用元：Wikipedia](https://ja.wikipedia.org/wiki/Nagle%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)

* 遅延 ACK

```
TCPデータセグメントのストリームを受信中のホストは、データセグメントを受信するごとに一つのACK(確認応答)セグメントを返す形式よりもACKセグメントを少なく送信することで、インターネットとホストの両方の効率を高めることができる。
```

[引用元：RFC1122](https://jprs.jp/tech/material/rfc/RFC1122-ja.txt)

これらの機能や遅延の発生条件を詳しく説明すると長くなるため発生するとどうなるをざっくりと書くと……

データ送信側：応答が来ていないデータがあるから一旦送信を待とう
データ受信側：データ細かく来たから応答は次のデータが来たらまとめて返そう

という形で、双方がお見合いとなり、タイムアウトするまで待機してしまうことがあります。（約200ms）
そして、この動作が頻発すると転送の著しい遅延に繋がります。

なお、遅延ACKのルールには以下のようなものが定義されています。

```
フルサイズ(訳注: 実効送信MSSバイト)のセグメントのストリームの場合、少なくとも2個ごとにACKセグメントが返されるべきである。
```
[引用元：RFC1122](https://jprs.jp/tech/material/rfc/RFC1122-ja.txt)

このルールを活かし遅延ACKが極力発生しないよう、HULFT8では転送データをMSS×2の倍数で設定しています。

# まとめ

HULFT8の転送速度が早い理由をまとめると、以下の2つとなります。

* TCP/IPの情報からMSSを取得し、無駄なパケット分割がされない最適なサイズでネットワークに書き込んでいる
* Nagle アルゴリズム/遅延ACKによる転送遅延が発生しにくいデータサイズを書き込んでいる

HULFT8の転送が早い理由の１つが分かって頂けましたでしょうか？
他にも転送速度を上げるための仕組みがありますが、また機会があれば記載できればと思います。