[iOS] SCNetworkReachabilityGetFlags のブロックの件

2011年7月8日金曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

以前、Cocoaの日々: ネットワーク接続状況を知る[2] SCNetworkReachabilityGetFlags はブロックする という記事を書いたがそのブロックの原因がわかった。


SCNetworkReachabilityCreateWithName


以前は、SCNetworkReachability のインスタンスを作るのにホスト名ベースの関数を使っていた。こんな感じ。
SCNetworkReachabilityCreateWithName(kCFAllocatorDefault,
   [@"xcatsan.com" UTF8String]);
この後、接続性をチェックする為に SCNetworkReachabilityGetFlags() を呼び出すとブロックするケースがあった、というのがその記事の内容だったのだが、その理由は DNSによる名前解決に時間がかかっていた為と思われる。

通常は 3G か Wifi がつながればインターネットへの接続ができて DNSによる名前解決が成功するのでこのブロック現象は起きない。ただモバイルルーターを使っていて、例えば
iPhone --◯--> Wifiルータ --×--> 3G
となっていた場合、iPhoneから見るとネット(Wifi)接続OKだが実際にはインターネットにつながっていない。地下鉄で利用しているとこんなケースがありうる。
この時に先ほどのホスト名ベースの関数 SCNetworkReachabilityCreateWithName() を使うと SCNetworkReachabilityGetFlags() を呼び出した時に、そのホスト名の名前解決を試みようとする。インターネットにはつながっていないので名前解決は当然失敗するのだが、Wifiネットワークには繋がっているので名前解決の処理を待ち続ける。その結果、タイムアウトが発生するまで SCNetworkReachabilityGetFlags() でブロックが起きる。

なお、そもそも Wifiルータへ接続できない場合は名前解決を試みないのでこの現象は起きない。
iPhone --×--> Wifiルータ --×--> 3G

また同じ記事で書いていた「初回のタイムラグ」はこの DNSの名前解決にかかる時間だと思われる。なお名前解決したからといってICMPなどを使って実際に到達可能性を調べているわけではない。


SCNetworkReachabilityCreateWithAddress


SCNetworkReachability のインスタンスを作る関数は他にも何種類かある。そのなかには IPアドレスを指定する関数もある。
SCNetworkReachability.h

SCNetworkReachabilityRef
SCNetworkReachabilityCreateWithAddress  (
      CFAllocatorRef   allocator,
      const struct sockaddr  *address
      )    __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
この関数を使うとDNS名前解決が発生しないので、それを原因とする SCNetworkReachabilityGetFlags() におけるブロッキングが発生しない(はず)。使い方はこんな感じ。
struct sockaddr_in sockaddr;
        bzero(&sockaddr, sizeof(sockaddr));
        sockaddr.sin_len = sizeof(sockaddr);
        sockaddr.sin_family = AF_INET;
        inet_aton("0.0.0.0", &sockaddr.sin_addr);        

        reachability_ =
            SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *) &sockaddr);
なおこの IPアドレスは接続性のチェックには使われていないようで適当なもの例えば 0.0.0.0 でも動作する。


DNS名前解決


DNSの利用について簡単な確認をやってみた。以下、結果。
a) iPhoneのWifi設定で DNS設定あり
  ホスト名ベース   => 接続 OK
  IPアドレスベース => 接続 OK

 b) iPhoneのWifi設定で DNS設定なし(設定項目を空)
  ホスト名ベース   => 接続 NG
  IPアドレスベース => 接続 OK

他の現象と合わせ、やっぱりホスト名ベースの場合は DNS名前解決を試みていると思われる。


NetworkReachability のまとめ


これまでの試行錯誤でわかった結果をまとめておく。
・デバイスの接続状態のみを判断する
・3G 接続できていれば、接続可能
・Wifi接続できていれば、接続可能(その場合、3Gより優先される)
・指定したホストに ping(ICMP)は飛ばしていない
・APIへホスト名を渡した場合、デバイスが接続可能なら名前解決を実行する
・名前解決に時間がかかると SCNetworkReachabilityGetFlags() はブロックする
 (名前解決のタイムアウトは実測値で 60秒程度)
・APIへIPアドレスを渡す場合、IPアドレスが実在しなくても接続チェックに影響を与えない。
  (例)0.0.0.0


ライブラリ


これらを受けて以前公開したライブラリを修正した。
[参照] Cocoaの日々: [iOS] ネットワーク接続状況取得ライブラリを公開

最新コードはこれ。
dev5tec/FBNetworkReachability - GitHub
以前は非同期(通知)ベースだったが、新バージョンでは同期、非同期の両方で使える。

以前のコードはここに残してある。
dev5tec/FBNetworkReachability at v1 - GitHub

Responses

Leave a Response

人気の投稿(過去 30日間)