SystemConfiguration.framework でネットワークの変化を知る
前回、SystemConfiguration.framework の DynamicStore からネットワークの状態を得る方法を書きましたが、今回はさらにその変化を検出して処理を行う方法です。すぐ次のエントリで書こうと思っていたのに遅くなってしまいました。
ネットワーク環境の変化は、コンピュータの名称が変わったり、ネットワークの接続環境が変わったり、ということが考えられます。前者はシステム環境設定での変更によるものですし、後者はイーサネットケーブルが抜けたり、無線 LAN の ON / OFF や弱電界での切断、ノートタイプの場合のスリープ復帰などなど、結構あります。
ネットワークを利用するアプリケーションでは、動作中にネットワークの環境が変わったら、アプリケーションとしてそれに応じた振る舞いや動作の変化をさせるのが望ましく、今回の説明はそのためにネットワーク状況の変化を検出する方法です。
Cocoa (Objective-C) ではよく NSNotificationCenter にオブザーバとして登録して通知を受けます。C言語で実装するものの、それと同様の方式で取得します。
サンプルなソースの全体像はこんな感じ。エラー処理と終了時のお片付け処理は省略してあるので脳内補完してください。
- static void _CallbackFunc(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info);
- @interface NetTest
- {
- SCDynamicStoreRef scDynStore;
- SCDynamicStoreContext scContext;
- CFRunLoopSourceRef runLoopSource;
- }
- - (void)setupSC;
- @end
- @implementation NetTest
- - (void)setupSC
- {
- CFStringRef keys[2];
- CFArrayRef keyArray;
- scContext.info = self;
- scDynStore = SCDynamicStoreCreate(NULL, (CFStringRef)[[NSBundle mainBundle] bundleIdentifier], _CallbackFunc, &scContext);
- runLoopSource = SCDynamicStoreCreateRunLoopSource(NULL, scDynStore, 0);
- keys[0] = SCDynamicStoreKeyCreateHostNames(NULL);
- keys[1] = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
- keyArray = CFArrayCreate(NULL, (const void**)keys, 2, NULL);
- SCDynamicStoreSetNotificationKeys(scDynStore, keyArray, NULL);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
- CFRelease(keyArray);
- CFRelease(keys[0]);
- CFRelease(keys[1]);
- }
- {
- unsigned i;
- for (i = 0; i < [changedKeys count]; i++) {
- NSLog(@"SystemConfiguration key[%d] '%@' is changed!", i, key);
- // 各種処理
- // (キーの値を取り出したり、画面の表示を更新したり)
- }
- }
- @end
- void _CallbackFunc(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
- {
- NetTest *self = (NetTest*)info;
- }
まず、前回と異なるのは DynamicStore を作る SCDynamicStoreCreate()
関数の第三引数に、 NULL
ではなくコールバック関数 _CallbackFunc
を設定しています。 _CallbackFunc
は先頭のプロトタイプ宣言のとおり、DynamicStore と、変更があったキーの一覧、そしてコンテキストの情報が渡ってきます。最後のコンテキスト情報 (info
) は、パラメタで渡している context.info
が設定されることになっていて、DynamicStore はその内容を関知していません。コールバック関数で利用するために、self
を設定しておきます。
続いて、実際に変更があったらコールバックを呼び出すための設定処理をしています。
監視したいキーを CFArrayRef にして登録します。キーは前回みたとおり、scutil
で確認した中から、必要なものを選んで監視するとよいと思います。RunLoopSource まわりの手続はおまじないと思っておけばいいかと。
これで、登録しておいたキー項目に変化があると、コールバック関数が呼ばれるようになります。コールバック関数は、NetTest
クラスのメンバではない単なる C の関数ですので、コールバック関数の info
に設定しておいたクラスのインスタンスをキャストしてそのメソッドを呼び出して処理させます。
実際の処理は、ループして変化したキーをチェック、キーの値を DynamicStore から取り出して、適切な処理や GUI への操作を行う、ということになります。
まとめると以下でしょうか。
- コールバック関数を登録する
- 監視したいキーのリストを用意して登録する
- RunLoopSourceを追加する
- コールバック関数から処理メソッドを呼び出す
- 処理メソッド内でキーを判定して応じた処理をする
ちょっと駆け足でしたが、こんな感じです。