TCPソケットの持続的接続
はじめに
TCPソケットを使用して通信するプログラムを作成する場合、よく用いられるのは送信の都度、送信側から受信側に接続(コネクト)する方法です(図−1)。コネクトに成功すると一連の通信を行ってからコネクションを切断(クローズ)します。
公開ソフトウェア「碁 Ver 5.0」もこの方法で通信を行っています。
この方法は実装が簡単で通信の信頼性も高く安定しています。ただ、この方法はコネクトのオーバーヘッドを考慮しなければならないような場合に問題があると言われています。またインターネット(WAN)を経由して通信する場合、パソコンが接続されているLANとWANの間にルーターがあるとルーターのポートマッピング(静的IPマスカレード)の設定が必要です。
両方のパソコンがルーターを使用している場合は、両方のルーターでポートマッピングの設定が必要になります。(ポートマッピングについては
こちらをご覧ください)
これ以外の方法として一方のパソコンから接続(コネクション)を確立し、このコネクションを使用して双方向の通信をする方法があります(図−2)。最初に一方からコネクトし、通信の必要がなくなるまでコネクションを維持します(持続的接続:Persistent Connection)。コネクションを維持している間はそれぞれのパソコンが自由にメッセージを送信します。
この方法はコネクトのオーバーヘッドがないことと、ルーターがある場合でも一方のルーターのみポートマッピングの設定をすればよいことがメリットです。もしどちらかのパソコンがルーターを使用せず、直接WANに接続しているのなら、ルーターを使用している側からコネクトすればポートマッピングの設定を全く行わずに済みます。
図−1 都度接続 |
|
図−2 持続的接続 |
|
|
|
持続的接続のテスト
GARAさんが使っているアプリケーションの中には図−2のような接続を使用していると思われるものがいくつかあるので、どのようにして実現しているのかネットで情報を探してみましたがピッタリしたものを見つけることができませんでした(探し方が悪い?)。そこでテストプログラムを作成して信頼性等を確認することにしました。テストの目標は非同期に両方のパソコンからそれぞれ1000回送信し、受信側はメッセージを漏れなく受信できることです。
テストプログラムは通信処理部をスレッドにして、送信データの作成と送信指示を上位のプログラムで行うことにしました。
最初のテストプログラム
まず、次のフローで動作するプログラムを作成してテストしてみました。
|
|
1.通信スレッドの概略フローです。 2.一方のパソコンをコネクト側、もう一方のパソコンを リッスン側とします。 3.送信データは上位のプログラムで作成します。 4.「送信メッセージ作成」は送信データにヘッダの付加等を 行います。 5.「受信メッセージ処理」はヘッダの除去、受信したことを 上位のプログラムに通知する等を行います。
|
テストの結果、一方のパソコンからのコネクトだけで通信ができることを確認できましたが、この処理フローでは次の3つの問題がありました。
1.途中でコネクションが切れた場合、以後通信ができなくなる。(GARAさんのパソコンは無線LANを使用していますが、無線アクセスポイントとパソコンの間の見通しが良くないため一時的な切断がよく発生します。)
2.リッスン側は一度受信するまで送信ができない。
3.回線の状態が悪いとき、送信側の send() は正常終了しているのに受信側では受信できないことがある。
改良版のテストプログラム
上記の問題に対応するため、次の処理を加えました。
1.コネクションが切れたときは再コネクトを試みる。
2.コネクト側はコネクト直後に開始メッセージを送信する。リッスン側は開始メッセージの受信でソケットディスクリプタを生成。
3.データの受信漏れを検出するため応答を設ける。
改良版のフローは次のようになります。
|
|
1.通信スレッドの概略フローです。 2.一方のパソコンをコネクト側、もう一方のパソコンを リッスン側とします。 3.送信データは上位のプログラムで作成します。 4.「送信メッセージ作成」は送信データにヘッダの付加等を 行います。 5.「受信メッセージ処理」はヘッダの除去、受信したことを 上位のプログラムに通知する等を行います。
6.受信したときは送信側に応答を返します。
7.送信側は応答を受信できないときは再送します。
|
このフローの実現には次の処理の実装が必要です。
1.適切なコネクション断の検出。
2.タイマ管理(コネクト、リッスン、recv()、応答受信待ち)。
3.再送回数管理。
4.応答受信とメッセージ受信が接近している場合、1回の recv() で両方が取得されるので適切に処理。
改良版のテスト結果
改良版でテストしたところ、目標の1000回の送受信を達成できました。また、短時間の通信障害が発生してもデータを漏れなく受信できることも確認できました。
送信1600回、受信2500回に達したときです |
|
通信障害を乗りきったときです |
|
|
|
テストの環境
1.使用したパソコン
持続的接続のテストで動作確認を行ったパソコンのOSは Windows 2000 / Windows XP / Windows Vista / Window 7 です。
2.ソケットライブラリ
使用したソケットライブラリはバージョン 2.0 です。
3.使用したソケット関数一覧
No | 関数 | 記事 |
1 | WSAStartup() | |
2 | WSACleanup() | |
3 | WSAGetLastError() | |
4 | socket() | |
5 | connect() | |
6 | bind() | |
7 | listen() | |
8 | ioctlsocket() | ブロックモード/ノンブロックモードの設定 |
9 | accept() | |
10 | send() | |
11 | recv() | |
12 | closesocket() | |
テストプログラムはGUI型のプログラムですが、通信部分はスレッドにしたので Windows 特有の WSAAsyncSelect() は使用していません。
その他
1.持続的接続のプログラムは都度接続の場合と比較するとかなり複雑なものになりました。実用に耐える通信障害対応を実装した場合、開発工数は都度接続の10倍以上になると思います。
2.テストに当たっては電子レンジが役立ちました。銚子に水を入れて電子レンジを回すとかなり高い確率で無線LANの遅延や断が発生し、いろいろな通信障害パターンをテストすることができました。
3.ルーターのポートマッピングの設定も期待したとおりでした。一方のパソコンがWANに直接接続している場合は全く不要、両方のパソコンがルーター経由でWANに接続している場合は片方のルーターの設定だけでOKでした。
4.テストしているとき、上位のGUIアプリケーションが起動後1〜3時間でフリーズしてしまうことがありました。それでも通信スレッドは正常に動き続けていました。Windows は Windows Update をするようにとのメッセージを出し、イベントビューアで見ると ntdll.dll で問題が発生となっていました。しかし原因は通信スレッドの受信処理でプログラムに甘いところがあったことでした。スレッドを使用するプログラムのテストの難しさを改めて思いました。
このTipsについて
1.このTipsはGARAさんの知識や理解の不足による誤りがあるかもしれません。このためTipsの使用により万一損害が生じましても責任を負いかねますのでご了承ください。また、誤りや記述ミスにお気付きの場合はメールでご連絡いただければ幸いです。
HOME TOP