监听网络改变, 实时获取网络连通性,ping/发请求

Posted 想名真难

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了监听网络改变, 实时获取网络连通性,ping/发请求相关的知识,希望对你有一定的参考价值。

可以使用AFN框架中的AFNetworkReachabilityManager来监听网络状态的改变,也可以利用苹果提供的Reachability来监听。在开发中直接使用AFN框架处理更方便。

示例代码如下:

-(void)afn

    //1.创建网络状态监测管理者
    AFNetworkReachabilityManager *manger = [AFNetworkReachabilityManager sharedManager];

    //2.监听改变
    [manger setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) 
        /*
         AFNetworkReachabilityStatusUnknown          = -1,
         AFNetworkReachabilityStatusNotReachable     = 0,
         AFNetworkReachabilityStatusReachableViaWWAN = 1,
         AFNetworkReachabilityStatusReachableViaWiFi = 2,
         */
        switch (status) 
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"3G|4G");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WiFi");
                break;
            default:
                break;
        
    ];
    //3.开启监听
    [manger startMonitoring]; 

Reachablity 是一个ios下检测设备网络环境用的库, 苹果对SCNetworkReachabilityRef的封装写的demo,我们可以直接使用, 下载源码后拖入工程即可。

苹果官方提供的Doc,下载地址:Documentation Archive,  github地址

示例代码如下: 因为Reachablity只能监听网络状态的状况,不能监听它的改变,所以采用通知的方式
-(void)viewDidLoad

    [super viewDidLoad];

    //注册通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityStatusChange) name:kReachabilityChangedNotification object:nil];

    //创建对象
    Reachability *reachability = [Reachability reachabilityForInternetConnection];
    [reachability startNotifier];
    self.reachability = r;

-(void)reachabilityStatusChange

    if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == ReachableViaWWAN) 
        NSLog(@"3G|4G, %@",[self getNetType]);
        return;
    
    if ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == ReachableViaWiFi) 
        NSLog(@"wifi");
        return;
    
    NSLog(@"未知");


-(void)dealloc

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [self.reachability stopNotifier];

2g/3g/4g/5g的判断可以参考getNetType方法

#import <CoreTelephony/CTTelephonyNetworkInfo.h>

+ (NSString *)getNetType

    CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
    NSString *currentStatus = info.currentRadioAccessTechnology;
    NSString *currentNet = @"";
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyGPRS]) 
        currentNet = @"GPRS";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyEdge]) 
        currentNet = @"2.75G EDGE";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyWCDMA])
        currentNet = @"3G";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyHSDPA])
        currentNet = @"3.5G HSDPA";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyHSUPA])
        currentNet = @"3.5G HSUPA";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMA1x])
        currentNet = @"2G";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0])
        currentNet = @"3G";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA])
        currentNet = @"3G";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB])
        currentNet = @"3G";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyeHRPD])
        currentNet = @"HRPD";
    else if ([currentStatus isEqualToString:CTRadioAccessTechnologyLTE])
        currentNet = @"4G";
    else if (@available(iOS 14.0, *)) 
        if ([currentStatus isEqualToString:CTRadioAccessTechnologyNRNSA])
            currentNet = @"5G NSA";
        else if ([currentStatus isEqualToString:CTRadioAccessTechnologyNR])
            currentNet = @"5G";
        
    
    return currentNet;


+ (NSString *)networkStatus
    Reachability *reachability   = [Reachability reachabilityWithHostName:@"www.apple.com"];
    NetworkStatus internetStatus = [reachability currentReachabilityStatus];
    NSString *net = @"WIFI";
    switch (internetStatus) 
        case ReachableViaWiFi:
            net = @"WIFI";
            break;
        case ReachableViaWWAN:
            net = [self getNetType];   //判断具体类型
            break;
        case NotReachable:
            net = @"当前无网路连接";
        default:
            break;
    
    return net;

AF和 Reachablity都是对SCNetworkReachabilityRef的封装, 实际工程中使用AF还是更方便的.


上面的2种方式在底层原理上都是通过SCNetworkReachability来实现网络检测的, 官方的注释也写的很清楚, 只是告诉你当前使用的是什么网络, 但是不能保证能连接到xx服务器. 也无法检测实际网络连接情况。

"Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN."

可访问性无法告诉您的应用程序是否可以连接到特定主机,只能告诉您有可能允许连接的接口,以及该接口是否是WWAN。无法检测实际的网路情况

想要检测真正的网络状态, 目前想到的比较合理的方案有2个.

  • 通过发起一个真正的请求, 这个请求尽可能的轻巧, 尽可能的快,目前找的是一个淘宝的获取时间戳的接口. 这个接口最好是作为app的启动配置项, 如果地址失效了, 服务器在配一个其他的, 或者服务器自己提供类似的接口也可以.
    http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp
  • 通过ping 服务器地址来实现, 但是ping有一个问题, ping是基于ip协议的icmp协议, 可能会出现服务器是可以ping通的, 但是正常的接口是失败的.

正常网络

无网

设置代理,代理关闭

设置代理,代理开启

手机开热点,有网

手机开热点,无网

ping通过不通过通过通过通过不通过
接口通过不通过不通过通过通过不通过
正常接口通过不通过不通过通过通过不通过

ping是ICMP协议中常用网络管理命令.
ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。
ICMP使用IP的基本支持,但是,ICMP实际上是IP的一个组成部分,必须由每个IP模块实现。

手机设置代理是配置HTTP协议的, 无法把ICMP协议也指定.

所以上面出现了设置代理, 代理关闭的情况下, ping能通, 而接口不能通过.

ping方案, 使用苹果提供的demo的SimplePing, 然后封装了一下, 调用如下

    ULPingHelper *pingHelper = [[ULPingHelper alloc] init];
    pingHelper.host = @"www.baidu.com";
    [pingHelper pingWithBlock:^(BOOL isSuccess, NSString * _Nonnull ipString, NSTimeInterval latency) 
        if (isSuccess) 
            NSLog(@"成功 %@ %@  %d ms",pingHelper.host,ipString,(int)latency);
         else 
            NSLog(@"失败 %@ %@  %d ms",pingHelper.host,ipString,(int)latency);
        
    ];

发请求方案, 使用了NSURLSession,  注意发起的Request一定要设置缓存策略为NSURLRequestReloadIgnoringCacheData, 有可能get请求会被系统缓存, 出现网络其实不通, 但是本地有数据, 缓存返回了本地数据, 出现错误的判断.

+ (void)requestAction 

    // 与接口表现一致
    // 接口有失效风险,能换成自己的服务器最好
    NSLog(@"-- 开始");
    NSURL *url = [NSURL URLWithString:@"http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2];
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) 
        if (error) 
            NSLog(@"-- 出错");
         else 
            NSLog(@"-- 正常");
        
    ] resume];


 2种方案的demo地址:  https://github.com/guochaoshun/NetWorking

参考文章: 

iOS中ping-SimplePing的使用

iOS 检测网络是否真正可用(连通)的方法 - 发送请求

 iOS下的实际网络连接状态检测

ping方案中中读取ping的服务器ip地址,  GitHub - lovesunstar/STPingTest

以上是关于监听网络改变, 实时获取网络连通性,ping/发请求的主要内容,如果未能解决你的问题,请参考以下文章

ping测试某网段网络连通性

10.9 ping:测试主机之间网络的连通性

ping---测试主机之间网络的连通性

#yyds干货盘点#ping:测试主机之间网络的连通性

怎么用ping命令测试服务器网络连通性

ping命令使用介绍及怎样使用Ping命令来测试网络连通性