ホーム > Mac, Objective-C, 開発 > SystemConfiguration.framework でネットワークの変化を知る

SystemConfiguration.framework でネットワークの変化を知る

2010 年 02 月 10 日 コメントをどうぞ コメント

前回、SystemConfiguration.framework の DynamicStore からネットワークの状態を得る方法を書きましたが、今回はさらにその変化を検出して処理を行う方法です。すぐ次のエントリで書こうと思っていたのに遅くなってしまいました。

ネットワーク環境の変化は、コンピュータの名称が変わったり、ネットワークの接続環境が変わったり、ということが考えられます。前者はシステム環境設定での変更によるものですし、後者はイーサネットケーブルが抜けたり、無線 LAN の ON / OFF や弱電界での切断、ノートタイプの場合のスリープ復帰などなど、結構あります。

ネットワークを利用するアプリケーションでは、動作中にネットワークの環境が変わったら、アプリケーションとしてそれに応じた振る舞いや動作の変化をさせるのが望ましく、今回の説明はそのためにネットワーク状況の変化を検出する方法です。

Cocoa (Objective-C) ではよく NSNotificationCenter にオブザーバとして登録して通知を受けます。C言語で実装するものの、それと同様の方式で取得します。

サンプルなソースの全体像はこんな感じ。エラー処理と終了時のお片付け処理は省略してあるので脳内補完してください。

[codesyntax lang=”objc” tab_width=”4″ title=”DynamicStore 通知サンプル” lines=”normal”]

static void _CallbackFunc(SCDynamicStoreRef	store, CFArrayRef changedKeys, void *info);

@interface NetTest
{
	SCDynamicStoreRef	scDynStore;
	SCDynamicStoreContext	scContext;
	CFRunLoopSourceRef	runLoopSource;
}

- (void)setupSC;
- (void)updateSC:(NSArray*)changedKeys;
@end

@implementation NetTest

- (void)setupSC
{
	CFStringRef	keys[2];
	CFArrayRef	keyArray;

	memset(&scContext, 0, sizeof(scContext));
	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]);
}

- (void)updateSC:(NSArray*)changedKeys
{
	unsigned i;
	for (i = 0; i < [changedKeys count]; i++) {
		NSString* key = (NSString*)[changedKeys objectAtIndex:i];
		NSLog(@"SystemConfiguration key[%d] '%@' is changed!", i, key);
		// 各種処理
		// (キーの値を取り出したり、画面の表示を更新したり)
	}
}

@end

void _CallbackFunc(SCDynamicStoreRef	store, CFArrayRef changedKeys, void *info)
{
	NetTest *self = (NetTest*)info;
	[self updateSC:(NSArray*)changedKeys];
}

[/codesyntax]

まず、前回と異なるのは DynamicStore を作る SCDynamicStoreCreate() 関数の第三引数に、 NULL ではなくコールバック関数 _CallbackFunc を設定しています。 _CallbackFunc は先頭のプロトタイプ宣言のとおり、DynamicStore と、変更があったキーの一覧、そしてコンテキストの情報が渡ってきます。最後のコンテキスト情報 (info) は、パラメタで渡している context.info が設定されることになっていて、DynamicStore はその内容を関知していません。コールバック関数で利用するために、self を設定しておきます。

続いて、実際に変更があったらコールバックを呼び出すための設定処理をしています。
監視したいキーを CFArrayRef にして登録します。キーは前回みたとおり、scutil で確認した中から、必要なものを選んで監視するとよいと思います。RunLoopSource まわりの手続はおまじないと思っておけばいいかと。

これで、登録しておいたキー項目に変化があると、コールバック関数が呼ばれるようになります。コールバック関数は、NetTest クラスのメンバではない単なる C の関数ですので、コールバック関数の info に設定しておいたクラスのインスタンスをキャストしてそのメソッドを呼び出して処理させます。

実際の処理は、ループして変化したキーをチェック、キーの値を DynamicStore から取り出して、適切な処理や GUI への操作を行う、ということになります。

まとめると以下でしょうか。

  • コールバック関数を登録する
  • 監視したいキーのリストを用意して登録する
  • RunLoopSourceを追加する
  • コールバック関数から処理メソッドを呼び出す
  • 処理メソッド内でキーを判定して応じた処理をする

ちょっと駆け足でしたが、こんな感じです。


  1. コメントはまだありません。
  1. トラックバックはまだありません。