Home / ぼやきごと / 2010-04-08
2010-04-08

TCP通信プログラミング体験談

TCP通信プログラム実装時の体験談。

次のようなよくあるTCP送受信を行うプログラムを作りました。

  1. 【クライアント】ヘッダ情報(データサイズ情報を含む)を送信。
  2. 【クライアント】データを送信。
  3. 【サーバ】ヘッダ情報およびデータを受信。
  4. 【サーバ】データを処理し、結果データを作成。
  5. 【サーバ】ヘッダ情報(結果データサイズ情報を含む)を送信。
  6. 【サーバ】結果データを送信。
  7. 【クライアント】ヘッダ情報および結果データを受信。

送受信共にヘッダは20バイト未満、データと合わせても1KB未満のサイズで、サーバ側の処理も一瞬で終わるようなものです。

ところが、このプログラムの送受信1回あたりの所要時間を計測したところ、約300ミリ秒も掛かっていました。
3〜4回送受信するだけで1秒経ってしまう計算です。

何がボトルネックになっているのか細かく計測したところ、所要時間の9割以上は select 関数での受信待ち時間でした*1
ちなみに、 send 関数や recv 関数は一瞬で終わっていました。

そこで、次のようにヘッダとデータを一度に送信することで送信回数を減らしてみました。

  1. 【クライアント】ヘッダ情報(データサイズ情報を含む)およびデータを一度に送信。
  2. 【サーバ】ヘッダ情報およびデータを受信。
  3. 【サーバ】データを処理し、結果データを作成。
  4. 【サーバ】ヘッダ情報(結果データサイズ情報を含む)および結果データを一度に送信。
  5. 【クライアント】ヘッダ情報および結果データを受信。

要するに、今まで send(ヘッダ); send(データ); とやっていたところを send(ヘッダ+データ); にしてみたということです。
受信側の処理は変えていません。

そして再び送受信1回あたりに掛かる時間を計測してみました。
予想では半分の150ミリ秒くらいにはなるかと思っていましたが、結果はなんと約15〜30ミリ秒
分けて送っていた時の10倍以上もの速度になりました。

「TCP通信ではNバイトのデータを1回で送信しても1バイトずつN回に分けて送信しても所要時間は同じ」というような話をどこかで聞いたような気がするのですが、一体どうしてここまで差が出たのでしょうか…。
まだまだソケットプログラミングは勉強不足だなと感じた一件でした。

追記

前者の方法だと、送信側のヘッダ情報送信とデータ送信の間に受信側のヘッダ情報受信が挟まってしまって通信回数が増えているのではと予想。
つまり実際には次のような流れになってしまっているのではなかろうか。

  1. 【クライアント】ヘッダ情報(データサイズ情報を含む)を送信。
  2. 【サーバ】ヘッダ情報を受信。
  3. 【クライアント】データを送信。
  4. 【サーバ】データを受信。
  5. 【サーバ】データを処理し、結果データを作成。
  6. 【サーバ】ヘッダ情報(結果データサイズ情報を含む)を送信。
  7. 【クライアント】ヘッダ情報を受信。
  8. 【サーバ】結果データを送信。
  9. 【クライアント】結果データを受信。

上述した「TCP通信ではNバイトのデータを1回で送信しても1バイトずつN回に分けて送信しても所要時間は同じ」というのは、送信側が送信…というか正確には send 関数の呼び出しをすべて終えた後で受信側が recv 関数を呼んだ場合の話なのだろう。
送信側が send 関数を呼んだ時点ではまだデータは送信側の内部バッファに追加されるだけで、受信側が recv 関数を呼んだ時点で初めて送信処理が行われるので、それならば確かに send 関数を何度呼ぼうが所要時間はほぼ同じになる(内部バッファへのデータ追加に掛かる時間なんて無視できるレベルなので)。

とはいえ、これが速度に10倍もの差が付いた理由になるかは疑問だが…。

Category: [プログラミング][C++] - 2010-04-08 00:32:59

*1 ブロッキングしないようにタイムアウトを0ミリ秒指定した上でループさせています。