iOS Reachability detects network status

1. Overall introduction

  • The NSURLSession , NSURLConnection of network access, and webview related to web page loading have been introduced earlier , which basically satisfy the usual network-related development.
    In fact, there is more commonly used in network development is the detection of network status. Apple has high requirements for applications that need to be connected to the Internet, that is, it must be checked for network connection. In addition, when the network is abnormal, it can prompt the user that the network has been disconnected in time, rather than the program problem causing the freeze; when the user watches a video or downloads a large file, it prompts the user whether the current network status is mobile traffic or wifi, whether to continue to use , so as to avoid excessive traffic charges and so on without the user's knowledge.

  • There are many ways to detect the network status, and there are three commonly used methods.

 

Second, the use of Apple Reachability

It is very simple to use. It will be Reachability.hadded Reachability.mto the project and include Reachability.hthe header file where it is to be used. The sample code:

#import "Reachability.h"

/// 在刚开始就开始监听
- (void)viewDidLoad {
    [super viewDidLoad];
    // Reachability使用了通知,当网络状态发生变化时发送通知kReachabilityChangedNotification
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appReachabilityChanged:)
                                                 name:kReachabilityChangedNotification
                                               object:nil];
    // 检测指定服务器是否可达
    NSString *remoteHostName = @"www.bing.com";
    self.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
    [self.hostReachability startNotifier];
    // 检测默认路由是否可达
    self.routerReachability = [Reachability reachabilityForInternetConnection];
    [self.routerReachability startNotifier];
}
/// 当网络状态发生变化时调用
- (void)appReachabilityChanged:(NSNotification *)notification{
    Reachability *reach = [notification object];
    if([reach isKindOfClass:[Reachability class]]){
        NetworkStatus status = [reach currentReachabilityStatus];
        // 两种检测:路由与服务器是否可达  三种状态:手机流量联网、WiFi联网、没有联网
        if (reach == self.routerReachability) {
            if (status == NotReachable) {
                NSLog(@"routerReachability NotReachable");
            } else if (status == ReachableViaWiFi) {
                NSLog(@"routerReachability ReachableViaWiFi");
            } else if (status == ReachableViaWWAN) {
                NSLog(@"routerReachability ReachableViaWWAN");
            }
        }
        if (reach == self.hostReachability) {
            NSLog(@"hostReachability");
            if ([reach currentReachabilityStatus] == NotReachable) {
                NSLog(@"hostReachability failed");
            } else if (status == ReachableViaWiFi) {
                NSLog(@"hostReachability ReachableViaWiFi");
            } else if (status == ReachableViaWWAN) {
                NSLog(@"hostReachability ReachableViaWWAN");
            }
        }
        
    }
}
/// 取消通知
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}

There are two detections in the code: whether the default route is reachable, and whether the server is reachable. Many people may have doubts. It is enough to detect whether the network is connected. How can you also detect whether the server is reachable? Is the default route reachable?

In fact, although you are connected to the Internet, you may not be able to access the external network (usually the Internet). For example, if a router is connected, but the router is not connected to the Internet, it cannot be connected to the Internet. There is also the fact that when network data packets are transmitted in the Internet layer, one route to another route is called a hop. When it reaches 255 hops (most routes are set to 255) and has not reached the destination, the network data packets are discarded.

The router has a set of algorithms to ensure the optimal path, and a routing table (which saves the path table). If a packet does not have a matching path in the routing table, the router will send the packet to the default route , which is the default route here. It is whether the default route detected above is reachable. (It's quite complicated inside, so stop here)

What's frustrating: Reachability doesn't detect if the server is actually reachable, only if the device is connected to a local area network, and whether it's WiFi or WWAN. That is: turn off the device network, immediately detect NotReachable, connect to the router and immediately detect ReachableViaWiFi,,,,

deallocIf notification is used in the code, be sure to cancel the notification when releasing the object . We know that notifications cannot be communicated between processes, and which thread sends the notification executes in which thread. If you want to monitor in other threads, call the startNotifierfunction in other threads. The newly opened thread does not open the runloop message loop by default, so you need to open the runloop, as follows:

    // 被通知函数运行的线程应该由startNotifier函数执行的线程决定
    typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSString *remoteHostName = @"www.bing.com";
        weakSelf.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
        [weakSelf.hostReachability startNotifier];
        
        weakSelf.routerReachability = [Reachability reachabilityForInternetConnection];
        [weakSelf.routerReachability startNotifier];
        // 开启当前线程消息循环
        [[NSRunLoop currentRunLoop] run];
    });

Finally, if you want to cancel the detection, call the stopNotifier method.

[self.hostReachability stopNotifier];
[self.routerReachability stopNotifier];

 

3. Use of AFNetworkReachabilityManager

  • Use directly

Use CocoaPodsor directly download and add AFNetwork to your project. If it is only used AFNetworkReachabilityManagerand does not apply to other network functions, just add its .m and .h to the project. AFNetworkReachabilityManagerUsing the block method, it will be called when the network state changes, and the block's call AFN has limited it to the main thread. The following describes the direct use

#import "AFNetworkReachabilityManager.h"
- (void)afnReachabilityTest {
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        // 一共有四种状态
        switch (status) {
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"AFNetworkReachability Not Reachable");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"AFNetworkReachability Reachable via WWAN");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"AFNetworkReachability Reachable via WiFi");
                break;
            case AFNetworkReachabilityStatusUnknown:
            default:
                NSLog(@"AFNetworkReachability Unknown");
                break;
        }
    }];
    
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
}
  • Using AFHTTPSessionManager

When using the AFN network framework, in most cases, we will create a network intermediate singleton class when using AFNetwork to prevent too many changes when changing the network framework, such as replacing the previously used ASI, if there is an intermediate class , the replacement is very simple, just need to modify the intermediate class. [NetworkTools sharedManager];Can be called when using

/// 头文件
#import "AFHTTPSessionManager.h"

@interface NetworkTools : AFHTTPSessionManager
+ (instancetype)sharedManager;
@end

---------------------------------------------------------------------------------

/// .m文件
#import "NetworkTools.h"

@implementation NetworkTools
+ (instancetype)sharedManager {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        //#warning 基地址
        //        instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.bing.com"]];
        instance = [[self alloc] init];
    });
    return instance;
}
- (instancetype)init {
    if ((self = [super init])) {
        // 设置超时时间,afn默认是60s
        self.requestSerializer.timeoutInterval = 30;
        // 响应格式添加text/plain
        self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", nil];

        // 监听网络状态,每当网络状态发生变化就会调用此block
        [self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            switch (status) {
                case AFNetworkReachabilityStatusNotReachable:     // 无连线
                    NSLog(@"AFNetworkReachability Not Reachable");
                    break;
                case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
                    NSLog(@"AFNetworkReachability Reachable via WWAN");
                    break;
                case AFNetworkReachabilityStatusReachableViaWiFi: // WiFi
                    NSLog(@"AFNetworkReachability Reachable via WiFi");
                    break;
                case AFNetworkReachabilityStatusUnknown:          // 未知网络
                default:
                    NSLog(@"AFNetworkReachability Unknown");
                    break;
            }
        }];
        // 开始监听
        [self.reachabilityManager startMonitoring];
    }
    return self;
}
@end

 

Fourth, the use of third-party frameworks

This will be more convenient to use. There are two methods of block and notification, and it supports multi-threading. It will not be introduced in detail here. README.md has the usage method:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Allocate a reachability object
    Reachability* reach = [Reachability reachabilityWithHostname:@"www.bing.com"];
    
    // Set the blocks
    reach.reachableBlock = ^(Reachability*reach) {
        // keep in mind this is called on a background thread
        // and if you are updating the UI it needs to happen
        // on the main thread, like this:
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"REACHABLE!");
        });
    };
    
    reach.unreachableBlock = ^(Reachability*reach) {
        NSLog(@"UNREACHABLE!");
    };
    
    // Start the notifier, which will cause the reachability object to retain itself!
    [reach startNotifier];
}

 

problem solved

The three methods are similar, they are very sensitive in detecting whether the device is connected to the local area network and the connection method, but cannot detect whether the server is reachable. Because they are used under the hood SCNetworkReachability, SCNetworkReachabilityit sends a network packet to the server, but it does not confirm that the server actually received the packet. So, if we want to confirm if the server is reachable, we need to send a real network request. Or we use socket programming to establish a tcp link to detect (three-way handshake is successful), as long as the link is successful, the server is reachable. In this way, only the header of tcpip is sent, and the amount of data is minimal. If the network environment is poor, the connectfunction will block, so don't call the sample code under the main thread at the end, the example is as follows:

#import <arpa/inet.h>

/// 服务器可达返回true
- (BOOL)socketReachabilityTest {
    // 客户端 AF_INET:ipv4  SOCK_STREAM:TCP链接
    int socketNumber = socket(AF_INET, SOCK_STREAM, 0);
    // 配置服务器端套接字
    struct sockaddr_in serverAddress;
    // 设置服务器ipv4
    serverAddress.sin_family = AF_INET;
    // 百度的ip
    serverAddress.sin_addr.s_addr = inet_addr("202.108.22.5");
    // 设置端口号,HTTP默认80端口
    serverAddress.sin_port = htons(80);
    if (connect(socketNumber, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) == 0) {
        close(socketNumber);
        return true;
    }
    close(socketNumber);;
    return false;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325341547&siteId=291194637