現在の通信プロトコルには層がひとつ足りない話

昨日の.NETラボの飲み会で話した「通信プロトコルに層がひとつ足りない」の話をちょっとメモ書き。

Siverlight のサーバーアクセスは WCF だったり、ASP.NET MVC からのデータベースアクセスは Entity Framework だったりする訳ですが、内部的には、TCP/IP を生のプロトコルで通しています。正確には HTTP on TCP/IP ってことで、HTTP プロトコルな訳ですが、alive http であろうと、session 毎に切ってしまう http だろうと、ちとある意味で、アプリケーション層と プロトコル層(TCP/IP層) – 用語は、正確ではないのですが — が直結しているために、データのやり取り(特に Connection や 例外関係)に無理があります。

結論から言うとですね、H さんとか、I さんとか、F さんとか、で業務アプリで通信データのやり取りをすると、必ず、TCP/IP を 2 セッションはります。

TCP/IP は、サーバー/クライアント方式ですから、

Clinet -> Server

の接続が必須なんですね。これを等価にするためには、

Client -> Server
Server <- Clinet

の2セッションが必須になるわけです。何故、2セッション必要なのか、双方向通信にする必要があるのかというと、Server 側はから、なんらかの通知をしたいとき(例外発生、異常発生時)に、Client -> Server の一方向だけだと、Client がポーリングをしていない限り、通知を受け取れないのです。つまり、Client が Get しない限り、Server から緊急通知を受けれないというタイムラグが発生します。

なので、サーバーからクライアントへの通知のために、もう1本コネクションを使います。

で、この2つのセッションですが、それぞれの会社で独自の実装をしています。と言いますか、プロジェクト単位で独自の実装をしています。まぁ、開発標準的なものもあるんですが、結局、ライブラリを使って独自の使い方になっちゃう。

これをデータベースアクセスにして考えてみると、Siverlight の非同期通信と、DB アクセスの同期通信が微妙に交差してしまうのです。

  1. Siverlight で、WPF 経由で Web サーバーをコール
  2. 結果待ちになる
  3. Web サーバーが、DB へ同期通信
  4. Web サーバーが、Silverlight へ WPF 経由で結果を返す。
  5. Silverlight が、イベントを発生させる。

という流れになっています。

Silverlight は、一見、非同期通信をしているように見えますが、データベースアクセス的に同期通信に縛られるんですね。これは、.NET Framework プログラミングの第3版を読んでいて気づいたのですが、Silverlight の通信中に他のことができるようにする、ということと(UX的にという意味で)、DB アクセスをしてデータを拾ってくるという意味とは微妙に異なります。

なので、理想的な非同期通信を書き直すと、

  1. Silverlight で、WPF 経由で Web サーバーをコール
  2. Web サーバーは WPF をキャッシュする
  3. Web サーバーは DB へ同期通信
  4. Web サーバーは WPF キャッシュを利用して、Siverlight 自身へ接続
  5. そしてデータ通信
  6. Siverlight は、イベントを発生させる。

という流れになります。一見、先の流れと同じように見えますが、4 のところで、改めて接続しているところが違います。

1 本目のセッションでは、Silverlight から Web サーバーへの WPF コール
2 本目のセッションでは、Web サーバーから Siverlight へのデータコール

になります。
こうすると、Siverlight のほうは、データ受信待ちのポーリングが必要なくなるので、動作が軽くなります。

で、本来の「セッション」の意味づけとして、等価な双方向通信として、この 2 本のセッションをまとめて「セッション」と呼ぶような層が必要、ということなのです。

擬似コードで言うとこんな感じですね。


private DualConnection _cn = new DualConnection

void Setup() {
	// イベントをセットアップ
	_cn.OnConnectin += OnConnection;
	_cn.OnRecv += OnRecv;
	_cn.OnClose += OnClose;
}
void Connect() {
	_cn = new DualConnection()
	// ホスト名を指定するが、どちらから接続してもOK
	_cn.Open( hostname );
}
void OnConnect() {
	// 相手から接続してきた場合
}
void OnRecv( byte[] data ) {
	// 受信データ
}
void OnClose() {
	// 切断処理
}

という形で定義しておいて、

  • Listen は、いらないので、いきなり OnConnection 後に OnRecv が入ってくる。
  • 送信は普通に _cn.Send のように送る。

という層をひとつ用意します。

マルチコネクションにする場合は、

void OnConnect( string hostname ) {
	// 相手から接続してきた場合
}
void OnRecv( byte[] data, string hostname ) {
	// 受信データ
}

のように、sender 要素を入れておくのも良いのですが、これだと受信側がマルチにならないので、実装的には勝手にスレッドを作成して、スレッド単位で P2P 的に接続するのがよいですね。

このあたりの実装を各社さん、それぞれが作っているので、双方向プロトコル on TCP/IP として実装されたらいいなあとか、なんとか。自分で作るか?

# P2P が似たような実装をしているはずなんですが、ちょっとコードを見たことないので分からず。

カテゴリー: 雑談, C# パーマリンク

現在の通信プロトコルには層がひとつ足りない話 への1件のコメント

  1. masuda のコメント:

    高尾さんの記事
    第1回 双方向通信を実現する代表的な技術
    http://thinkit.co.jp/story/2011/03/01/2025

コメントは停止中です。