2010年开始苹果清理了一批APP Store上的WIFI扫描软件, 缘由语焉不详.



这些WIFI扫描软件使用了苹果的私有函数apple80211.framework


尽管不能合法(指能通过App Store的审核)的获取WIFI列表, 不过我们还是可以获取到当前Wifi连接的信息,比如SSID.


SSID全称Service Set IDentifier, 即Wifi网络的公开名称.


苹果在IOS v4.1+版本上提供了公开的方法来获取该信息.



示范代码如下:


#import <SystemConfiguration/CaptiveNetwork.h>  
  
- (id)fetchSSIDInfo  
{  
    NSArray *ifs = (id)CNCopySupportedInterfaces();  
    NSLog(@"%s: Supported interfaces: %@", __func__, ifs);  
    id info = nil;  
    for (NSString *ifnam in ifs) {  
        info = (id)CNCopyCurrentNetworkInfo((CFStringRef)ifnam);  
        NSLog(@"%s: %@ => %@", __func__, ifnam, info);  
        if (info && [info count]) {  
            break;  
        }  
        [info release];  
    }  
    [ifs release];  
    return [info autorelease];  
}



对于ARC版本, 代码可简化如下:



- (id)fetchSSIDInfo {  
     NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();  
     NSLog(@"Supported interfaces: %@", ifs);  
     id info = nil;  
     for (NSString *ifnam in ifs) {  
         info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);  
         NSLog(@"%@ => %@", ifnam, info);  
         if (info && [info count]) { break; }  
     }  
     return info;  
}



一、公共WIFI综述

现在很多公司都在做免费WIFI,车站、公交、地铁、餐厅,只要是人员密集流动的地方就有WIFI,免费WIFI从最初的网页认证方式也逐渐向客户端认证方式偏移。本文主要讨论IOS认证上网的解决方案。 IOS端WIFI应用的相关开发,主要存在以下问题

1.IOS系统WIFI相关的接口很少,大部分接口都是私有接口
2.在设备连接上WIFI,没有通过路由器认证前,如果关闭IOS自动弹出的Portal页面,Iphone的WIFI会自动断开
3.如何禁止IOS系统自动弹Portal页面
4.公共WIFI的名称确定及不确定时的处理办法
本文主要讨论在使用公开的API,即可以提交到App Store的应用。

二、基础信息获取

1.获取网卡IP

+ (NSString *)localIPAddress
{
    NSString *localIP = nil;
    struct ifaddrs *addrs;
    if (getifaddrs(&addrs)==0) {
        const struct ifaddrs *cursor = addrs;
        while (cursor != NULL) {
            if (cursor->ifa_addr->sa_family == AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0)
            {
                NSString *name = [NSString stringWithUTF8String:cursor->ifa_name];
                if ([name isEqualToString:@"en0"]) // Wi-Fi adapter
                {
                    localIP = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
                    break;
                }
            }
            cursor = cursor->ifa_next;
        }
        freeifaddrs(addrs);
    }
    return localIP;
}

2.获取网卡信息

- (NSDictionary *)getWIFIDic
{
    CFArrayRef myArray = CNCopySupportedInterfaces();
    if (myArray != nil) {
        CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
        if (myDict != nil) {
            NSDictionary *dic = (NSDictionary*)CFBridgingRelease(myDict);
            return dic;
        }
    }
    return nil;
}

- (NSString *)getBSSID
{
    NSDictionary *dic = [self getWIFIDic];
    if (dic == nil) {
        return nil;
    }
    return dic[@"BSSID"];
}

- (NSString *)getSSID
{
    NSDictionary *dic = [self getWIFIDic];
    if (dic == nil) {
        return nil;
    }
    return dic[@"SSID"];
}



注意事项



在实际测试时,获取网卡信息getWIFIDic方法,在部分路由器上耗时很长(10秒以上),如果直接放在主线程中,会导致界面卡死。在认证相关的应用中,会根据网卡上的BSSID(例如:以特定3d:e6:c3开头的即为本公司架设网络)来判断是否属于本公司的路由。SSID、BSSID若为应用启动时必须获取的信息(我们公司的应用,就是这种),这个时候怎样处理呢?



放在异步线程,获取到网卡信息再初使化界面,这种方法依然会导致在某些路由器下初次打开界面超长时间的等待,我们的处理方法是,如果3秒内能够获取到相应的配置信息,直接根据配置信息初使化界面,在超过3秒时,给予默认的假WIFI信息,初使化界面。异步线程获取到真实的配置信息后,再重新更新界面。



三、认证过程中的棘手问题

1、Portal禁止弹出与WIFI自动关闭的问题

正常情况,用户使用Iphone手机连接带有Portal认证的路由器,在连接成功后,IOS系统会在已有列表中随机选择连接指定的网址(例如:www.itools.info)以测试当前路由器是否需要Portal认证。在需要Portal认证的网络,系统会弹出Portal页面,这个时候,如果用关掉portal页面,或者直接切换到其它应用,WIFI网络会直接自动断开(根本不给客户端认证机会^_^)。

我们的解决办法是路由器白名单,让路由器放行所有Portal测试的IP,以下为测试的域名:

www.appleiphonecell.com
captive.apple.com
www.itools.info
www.ibook.info
www.airport.us
www.thinkdifferent.us



对应的IP地址:


23.207.103.91
23.33.54.18
23.44.167.91
23.67.183.91
96.7.103.91
23.42.71.91
23.34.105.211
23.59.167.91
23.42.184.50
23.47.232.190
23.77.23.91
23.194.87.91
23.61.91.190
23.218.12.50
23.2.38.95
23.46.135.91
172.225.213.179
218.205.66.94
23.64.251.249
23.58.250.189



将以上所有IP加到路由器的白名单中,即可解决Iphone断开WIFI的问题,但是同时也不自动弹出Portal页面了,用户打开浏览器才会重定向到Portal页面。



2、WIFI名确定解决方法



如果公司部署的公共WIFI名确定的情况,就比较简单了,不需要配置上述白名单也可以保证WIFI不断开,具体办法是,在程序启动时,向IOS系统注册SSID,方法如下:


- (void)registerNetwork:(NSString *)ssid
{
    NSString *values[] = {ssid};
    CFArrayRef arrayRef = CFArrayCreate(kCFAllocatorDefault,(void *)values,
                                        (CFIndex)1, &kCFTypeArrayCallBacks);
    if( CNSetSupportedSSIDs(arrayRef)) {
        NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
        CNMarkPortalOnline((__bridge CFStringRef)(ifs[0]));
        NSLog(@"%@", ifs);
    }


}




四、总结

苹果对于WIFI这块公开的API非常少,在开发公共WIFI应用时会遇到各种问题