今回はPC間で通信を行うプログラムの実践です。
そんなプログラム作って危険じゃない?みたいな疑問については
ここまでの調査であらかた片付いたと思います。
LAN内でのみやり取りを行なう分には、外部に情報が漏れたりしないことがわかっています。
サーバーを不特定多数の人々へ公開しないというポリシーで
ネットワークプログラムを作っていきましょう。
それ意味あるのか…と思う方もいるかもしれませんが、いつか外部からアクセス出来るようにする時は
クラウドサービスでも借りて、乗っ取られても安全なPCに作ったプログラムを置けば良いのです。
では、何をするとネットワークプログラミングができるのか見て行きましょう。
C# WCF というものを使います。
Windows Communication Foundation です、WPF ではありません。
概要は普通に C# でクラスを作って、特殊な属性を付けるとネットワーク越しに
情報のやりとりが出来るようになるとか…なにそれ、超便利!
どうやって実現するの?っという情報は次のページを参考にして取得しました。
一箇所間違いがあり、書いた通りに行っても動作しませんが、その間違いにはすぐに気付けると思います。
ネームスペース解決の部分で / を . に書き換えるとうまくいきます。
以上です…としたいところですが、さらに噛み砕いて自分が欲しかった情報に書き換えてみようと思います。
いきなり実践して覚えるのは本当に何も知らない初心者中の初心者
たとえばC++やC#で基本的に何でもできるけどPC間でのやり取りを一切意識したことがないプログラマがやること
でも、ここまで読み進めた方はOSI参照モデル(ネットワーク基本中の基本)を具体的に理解できる人のはずなので
もうすこし、どうやってWCFが動くのか学んでから実践に入ったほうがずっと身につくはずです。
ということで、次のリンク先の情報から ABC というものが、どういうものか理解して下さい。
A はアドレスの A、サーバーマシン名またはIPアドレスのことですね。
B はバインドの B、プロトコルに何を使うかを定義します。
これから使おうと思うのはTCPのマシン間通信とプロセス間通信です。
C はコントラクトの C、受け渡しを行なうデータの部分ですね SOAP を理解すると納得できるかと
ABC を理解しました。
とりあえず一番簡単な?、一つの大きなファイル(3GBほど)を、宛先PCへ送るネットワークプログラムを作成して、
動作確認してみたいと思います。
適当に WPF で UI を作ってみますね。
はい WPF 使ったことある人なら 5分かそこらでこれくらいのUIは完成しますね。
相手 PC へ指定パスのファイル(フォルダならその下のファイル全て)を転送 or 取得するツールです。
まずは WCF 関係なしにインタフェース、クラス実装を書いてみます。
えーと、まずは空のサービスを作ります。
※しばらく WCF 関係ないです。
Service1 のまま作るのもおかしいと思いますので FileManagerService にリネームして進めます。
現在、エントリポイントは次のコードになっています。
サービスとは?みたいな細かい話は次のリンクで片付けてきて下さい。
これ以上ないくらい、わかりやすい説明…のはずです。
あと、ここから先は基本的に次のリンク先のチュートリアルをなぞるだけです。
困ったらリンク先で解決してください。
チュートリアル : コンポーネント デザイナによる Windows サービス アプリケーションの作成
続いて、サービスのインストーラを作成します。
先程も述べましたが、細かいことはリンク先のチュートリアルにすべて書いてあります。
同じことを書くのは無意味なので、ここには書きません。
簡単に作業ログを示すと、Service1 と書かれている部分はすべて FileManagerService と書き換えました。
FileManagerService.cs を開いた時のデザイナ画面にて、コンテキストメニューを表示し
インストーラの追加を選択します。
ここでも serviceProcessInstaller1, serviceInstaller1 などの名前があるので良い感じにリネームしました。
そして新規にセットアッププロジェクトを作成します。
プロジェクトのコンテキストメニューより、[追加]→[プロジェクト出力]を選択します。
プライマリ出力の項目を選択して先程ウィザードから作ったサービスのプロジェクトを選択して下さい。
今度は[表示]→[カスタム動作]でカスタム動作を追加します。
順々に進み
次の選択が行えるはずなので、そのまま選択してOKします。
成功すると次の画面に移行します。
そうそう、Service の FileManagerServiceInstaller の StartType を Automatic にすることを忘れずに
サービス登録後、PC再起動後に自動で開始されるようになります。
加えて、Service の FileManagerServiceProcessInstaller の Account を LocalSystem にすることを忘れずに
よし、ひとまずソリューションをビルドして、セットアッププロジェクトのコンテキストメニューから
インストールを選んでみて下さい。
次のセットアップウィザードが起動すればOK!
どんどん進めていくと…UAC確認の後、完了するはずです。
では、サービスがインストールされたのか確認してみましょう。
Windows メニューの[コンピュータ]→[管理]を実行します。
[サービスとアプリケーション]の[サービス]を選択して一覧を表示します。
あなたの作ったサービスが登録されていることが確認できましたでしょうか?
今のところ空のサービスですが、これに WCF ホスト機能を追加します。
ひとまず、アンインストールを行ってください。
プロジェクトのコンテキストメニューより選択できます。
新規プロジェクトを作成します。
やっと WCF の実践です。
作られたプロジェクトにて、適当にサービス名を編集します。
その後 App.config を WCF 設定の編集で開きます。
アドレスを次に示すようにネームスペース.クラス名にしてください。
現在のインタフェース定義ファイルと実装ファイルは次のとおりです。
属性の意味は、既に概念のところで学んでいます。
理解できない方は再度読みなおして下さい。
それでも理解できない方はあきらめましょう。
うまい具合にテストコードが実装されていますので、これをそのままサービスに組み込みましょう。
参照に System.ServiceModel を追加し
さらに ServiceLibrary プロジェクトを追加参照し
忘れてはならないのはセットアッププロジェクトのリビルドです。これで依存ライブラリもインストールされるようになります。
次にサービス起動時にServiceHost を Open 、サービス終了時に Close するコードを書きます。
具体例は次の通り
これまた面倒な作業なのですが、Library の app.config ファイルを
サービスのプロジェクトで改めて読み込むようにします。
既存項目の追加で、リンクとして追加するだけで良いです。(忘れるとエンドポイントが無いとか言われ例外が発生します)
これでひとまず localhost:8732 でエンドポイント GetData, GetDataUsingDataContract が呼べるようになるはずです。
一度ソリューションをリビルドして、サービスをインストールし、サービスを開始してみましょう。
ベースアドレス http://localhost:8732/nsFileManagerServiceLibrary.FileManagerServiceLibrary にアクセスすれば
サービスを作成しましたページを開くことが出来るはずです。
このままサービスが開いている状態で作業を続けます。
ここまで確認できたら次はクライアントアプリの作成です。
プロジェクトのコンテキストメニューからサービス参照の追加を選択
ソリューションから探索すると作ったサービスが一つ選択された状態になるので
適当なリファレンス名を付けてOKを押します。(別のアプリケーションが既にこの URL を…云々のメッセージは了解します。)
サービス参照が入ったのならば次のコードが記述できるはずです。
テスト用に CUI プロジェクトで作業しています。
サービスは起動中で、WCF サービスも開いているはずですので、このコードを実行すると
次の結果が得られるはずです。
やりました!ひとまず動いているみたいですね。
今は localhost で作業していますので、別の PC にサービスインストーラを入れてテストしてみましょう。
クライアントプロジェクトのapp.config にて localhost のところに相手PC名またはプライベートIPアドレスを記入します。
失敗しました。
セキュリティの関係上、例外が発生して失敗するはずです。
ディフォルトのバインディングは wsHttpBinding になっているからです。
例外の内容は…「呼び出し元はサービスによって認証されませんでした。」です。
セキュリティ機能を全て切るとうまくいきます。
暗号化も認証もせずに情報を送りつけるバインディングを選べばよいのです。
具体的には WCF サービスライブラリの app.config にて binding="basicHttpBinding" と
書き換えることでリモートのPC間でうまく動作します。
(※一度説明していますが、クライアントの app.config では相手PC名を利用すること)
セキュリティを勉強します。
Windows Communication Foundation セキュリティ
なかなかに覚えることが多いですね。
今度まとめられるほど理解したら、自分のためにメモを残そうと思います。
WCF サービスがセキュリティなし状態で「一応」動作しているので、巨大なファイルのやり取りというものを行ってみます。
次のページに本件に関わる情報がまとまっていたので、参考に試してみます。
上記インタフェースを実装して、指定パスのファイルストリームを取得したり
こちらからパスを予め設定して、ファイルストリームを渡してパスに書き込んでもらうなどの機能を付けます。
どのようにロジックを実装するかは説明しません。
ここまで読み進められる貴方であればどんなコードになるかは大体わかるはずです。
既に疑問に思っているかもしれませんが、ドキュメントにある通り実装に制約があります。
ストリーミングを引数で渡す場合は、Stream 以外の引数を指定できないということです。
ファイルパスとストリームを同時に渡すインタフェースは作っても正しく動作しません。(コンパイルエラーになる)
ここ↑に示すようにとりあえず 4MB のファイルのやり取りができるように書きました。
結果、ファイルのやり取りに成功!
4MBを超えるファイルは失敗することも確認しました。
今度は3GBまで大丈夫にしてひとまず 356MB のファイルが取得できるか確認します。
途中でタイムアウトしてしまいました。
6MB ほど受信しているあたりです。
どうやら1分で切られます。
そもそも6MBのファイルくらいなら、エクスプローラを介してコピーを行なうのに5秒ほどで終わります。
1分も使って 6MB しか送信できないとか、実用に耐えられない。
そう、何か見落としているに違いない。
ネットワーク環境を更新、様々な設定を更新。
TransferMode を Streamed に変更、SendTimeout を 1:00 から 10:00 に拡張します。
356MBほどの巨大なムービーデータを外部PCから取得する処理に成功しました。
(...1分以内に処理が完了しちゃった。)
このときのクライアント側の app.config ファイルを次に示します。
…1分以内に 356MB という巨大なデータの転送が完了しました。
ここで大事なことを書いていないことに気付きました。
相手先PCはノートパソコンで無線LANを経由して情報をやり取りしています。
その無線LAN環境については大きな変化があり、無線LANルータを最新のものに更新しました。
なんとも通信速度が60倍近く改善されたわけです。
最終試験として 2GB のファイルコピーも行ってみます。
約7分ほどでPC間コピーが完了しました。(すばらしい!)
つまり…上記設定のいずれかが起因していて(おそらくreceiveTimeout="00:10:00" sendTimeout="00:10:00")
巨大なデータ転送に耐えられる高速なネット環境を用意したことも転送時間短縮につながり
以前失敗していたコピーが成功するようになったということでしょう。
はい、簡単に BasicHttpBinding で巨大なファイル転送テストを行いました。
結果、意図した通りのスピードでファイルを正しくやり取りできることを確認しました。
ネットワークプログラムは.NETを利用することで、ここまで簡単になったわけです。
本当に素晴らしい!もう C# プログラムを書く感覚で外部PCと通信できるわけです。
今日示した内容を使えば相手先PCに行わせる作業はインストーラを叩いて再起動するだけ。
次回からはPCを起動するだけでサービスが開始されます。
意図したとおりです。完璧ですね。
次回は TCP を使ったバインディングに変更し、どれだけ高速化されるのか確認するのと
利用するセキュリティ関連の設定を確認します。
安全かつ高速に情報をやりとりする簡単なプログラム!
興味が湧いた方はぜひ次の回も読んでみてください。
※WCF は素晴らしい技術ですが、ここに示した情報を使って、他人のPCのフォルダをあさり
パスワードなどが記述されたテキストファイルを見つけて
密かに重要な機密情報を収集するといったスパイウェアなどは絶対に作ってはいけません。
有用なサービスを提供する裏でこのような処理が走るソフトが世の中に蔓延しているようです。
このようなソフトを使う前にしっかりとソフトの安全性について調査することも大切です。
C++ でも C# でも exe や dll のソースコードを閲覧することは可能です。(詳細は示しません)
貴方がリバース・エンジニアリングを禁止したとしても、規約を無視する第三者によって簡単に暴かれます。
コードを難読化しても処理の内容までは隠せません。
また通信内容の確認は可能です、暗号化していたところで、このようなスパイ行為を行なっていることが
見つかり次第貴方は社会から抹殺されることになります。
釘を刺します。
身内だけで使うプログラムだったとしても作ってはいけません。
決してスパイウェアは作らないように心がけてください。
ただし、アプリの不具合情報などをサポートセンターに送信するシステムはとても有用です。
アプリケーションサービスの開始時に情報提供に協力するか尋ねられることがあると思いますが
ここは貴方の情報をサポートセンターへ送信するように協力いただければと思います。
(何の情報を送るのかわからないので怖いですが、ここはアプリ提供元を信用するかどうかですね。)
2013/11/03 初記
2013/12/23 最終更新