在iOS上检测Internet连接的最简单方法?


147

我知道这个问题似乎是许多其他问题的重复,但是,我觉得这里没有简单地解释这个问题。来自Android和BlackBerry背景,HTTPUrlConnection如果没有可用的连接,立即发出请求失败。这似乎完全是理智的行为,我很惊讶地发现NSURLConnection iOS中没有模仿它。

我知道Apple(以及其他扩展了Apple的苹果)提供了一个Reachability类来帮助确定网络状态。我很高兴首先看到这一点,并完全希望看到类似bool isNetworkAvailable(),但是令我惊讶的是,我发现了一个复杂的系统,需要通知注册和回调,以及一些看似不必要的细节。肯定有更好的办法。

我的应用程序已经可以正常处理连接失败,包括没有连接。通知用户失败,应用程序继续运行。

因此,我的要求很简单:可以在所有HTTP请求之前调用单个同步函数,以确定是否应该打扰实际发送请求。理想情况下,它不需要设置,只返回一个布尔值。

在iOS上真的不可能吗?



您永远不要在每个HTTP请求之前调用同步网络可达性方法。除非可访问性异步告诉您网络状况发生了变化,然后您可以选择不发送HTTP请求,否则干这种工作是很疯狂的。这也是存在超时的全部原因(然后您应该适当地处理该情况)。请参阅上面的链接以了解异步方式
iwasrobbed

Answers:


251

我进行了一些研究,并使用最新的解决方案更新了答案。我不确定您是否已经看过它,但是Apple提供了一个不错的示例代码。

此处下载示例代码

在您的项目中包括Reachability.h和Reachability.m文件。看看ReachabilityAppDelegate.m上的示例,了解如何确定主机的可达性,WiFi,WWAN等的可达性。对于非常简单的网络可达性检查,您可以执行以下操作

Reachability *networkReachability = [Reachability reachabilityForInternetConnection];   
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];    
if (networkStatus == NotReachable) {        
    NSLog(@"There IS NO internet connection");        
} else {        
     NSLog(@"There IS internet connection");        
}

@BenjaminPiette:不要忘记将SystemConfiguration.framework添加到您的项目中。


46
不要忘记将SystemConfiguration.framework添加到您的项目中。
本杰明·皮耶特

1
@chandru有效。这是我发现的检测网络连接的最简单,最有效的方法。简单容易!
S1U 2014年

3
小心点。我发现,至少在模拟器中,当网络恢复时,它不能可靠地工作。我在关闭wifi的情况下启动,它正确地报告没有可用的网络;但随后我重新打开wifi,即使实际的网络查询正常,SCNetworkReachabilityGetFlags仍返回0。
Joe Strout 2014年

2
我相信我在下面的答案(stackoverflow.com/a/26593728/557362)需要较少的工作(无需下载其他文件),但这仅是我的看法。
GilesDMiddleton 2014年

3
请注意,这仅告诉您与特定主机的连接是否可路由。这并不意味着您实际上可以连接。示例:您将iPhone的wifi连接到相机的wifi。Wifi连接了默认网关:所有主机均报告可达。但实际上并非如此-相机是死胡同。
xaphod

45

鉴于此线程是此类问题的Google最佳搜索结果,所以我想我会提供对我有用的解决方案。我已经在使用AFNetworking,但是直到项目进行到,搜索都没有揭示如何完成此任务。

您需要的是 AFNetworkingReachabilityManager

// -- Start monitoring network reachability (globally available) -- //
[[AFNetworkReachabilityManager sharedManager] startMonitoring];

[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {

    NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));


    switch (status) {
        case AFNetworkReachabilityStatusReachableViaWWAN:
        case AFNetworkReachabilityStatusReachableViaWiFi:
            // -- Reachable -- //
            NSLog(@"Reachable");
            break;
        case AFNetworkReachabilityStatusNotReachable:
        default:
            // -- Not reachable -- //
            NSLog(@"Not Reachable");
            break;
    }

}];

您还可以使用以下命令同步测试可达性(一旦开始监视):

-(BOOL) isInternetReachable
{
    return [AFNetworkReachabilityManager sharedManager].reachable;
}

3
我赞扬了这一点AFNetworkReachabilityManager
Selvin 2014年

对于那些对此并不立即明白的人:AFNetworking是第三方库。
arlomedia

1
网络可达性是一种诊断工具,可用于了解请求失败的原因。它不应用于确定是否发出请求。-那就是文档所说的。
2015年

AFNetworkReachabilityManager.reachable不执行任何形式的同步检查。它返回一个逻辑或reachableViaWWANreachableViaWifi
jdolan

40

对不起,回复太晚了,但我希望这个答案可以在将来对您有所帮助。

以下是一个小的本机C代码片段,可以检查互联网连接,而无需任何其他类。

添加以下标题:

#include<unistd.h>
#include<netdb.h>

码:

-(BOOL)isNetworkAvailable
{
    char *hostname;
    struct hostent *hostinfo;
    hostname = "google.com";
    hostinfo = gethostbyname (hostname);
    if (hostinfo == NULL){
        NSLog(@"-> no connection!\n");
        return NO;
    }
    else{
        NSLog(@"-> connection established!\n");
        return YES;
    }
}

迅捷3

func isConnectedToInternet() -> Bool {
    let hostname = "google.com"
    //let hostinfo = gethostbyname(hostname)
    let hostinfo = gethostbyname2(hostname, AF_INET6)//AF_INET6
    if hostinfo != nil {
        return true // internet available
      }
     return false // no internet
    }

3
比Apple Reachability更好。但是根据文档,gethostbyname已过时,您应该使用getaddrinfo:struct addrinfo * res = NULL; int s = getaddrinfo(“ apple.com”,NULL,NULL,&res); bool network_ok =(s == 0 && res!= NULL); freeaddrinfo(res);
Tertium

1
虽然使用google.com来检查互联网的可用性是很常见的,但值得注意的是,它在某些国家/地区已被屏蔽。更好的选择是来自更多“政府友好”公司的域名,例如yahoo.com或microsoft.com。
Laurent

8
@Laurent:最好的选择是使用您要从中请求数据的服务器地址。google.com只是一个例子。
dispatchMain

7
这只是在进行DNS查找吗?如果是这样,缓存的DNS结果是否可能导致功能错误地认为网络可用?
Stephen Moore

2
@amar循环运行将消耗无线数据,您不想这样做。
含义-

31

我目前使用这种简单的同步方法,在您的项目或委托中不需要额外的文件。

进口:

#import <SystemConfiguration/SCNetworkReachability.h>

创建此方法:

+(bool)isNetworkAvailable
{
    SCNetworkReachabilityFlags flags;
    SCNetworkReachabilityRef address;
    address = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com" );
    Boolean success = SCNetworkReachabilityGetFlags(address, &flags);
    CFRelease(address);

    bool canReach = success
                    && !(flags & kSCNetworkReachabilityFlagsConnectionRequired)
                    && (flags & kSCNetworkReachabilityFlagsReachable);

    return canReach;
}

然后,如果您将其放在MyNetworkClass

if( [MyNetworkClass isNetworkAvailable] )
{
   // do something networky.
}

如果您正在模拟器中进行测试,请打开和关闭Mac的wifi,因为模拟器似乎会忽略电话设置。

更新:

  1. 最后,我使用了线程/异步回调来避免阻塞主线程。并定期重新测试,以便可以使用缓存的结果-尽管您应避免不必要地保持数据连接打开。

  2. 如@thunk所述,Apple会使用更好的URL。http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network


1
谢谢,与该线程中的大多数其他人一样:不要忘记将SystemConfiguration.framework添加到您的项目中。
eselk

1
奇怪的是,在我的Xcode项目中,我不必添加SystemConfiguration.framework。我有SpriteKit,UIKit,StoreKit,Foundation和CoreGraphics,但没有SystemConfiguration...。是否存在隐式包含?
GilesDMiddleton 2015年

3
这对我有用,但是当我在连接到没有Internet连接的wi-fi网络时运行它时,它锁定了我的UI约30秒钟。在这种情况下,需要异步方法。
arlomedia

2
www.apple.com是大型网站...更好的选择是www.google.com
Fahim Parkar,2015年

2
很好的答案!一个建议:测试URL www.appleiphonecell.com严格用于此目的,并且在中国显然没有被阻止。
Thunk

11

如果有可能,并且在完成实现时看一下它真的很简单,这又很简单-因为您只需要两个布尔变量:Internet可达性和主机可达性(您通常需要多个这些变量) )。一旦组装了可以确定连接状态的帮助器类,您实际上就不会再在乎了解这些过程所需的实现了。

例:

#import <Foundation/Foundation.h>

@class Reachability;

@interface ConnectionManager : NSObject {
    Reachability *internetReachable;
    Reachability *hostReachable;
}

@property BOOL internetActive;
@property BOOL hostActive;

- (void) checkNetworkStatus:(NSNotification *)notice;

@end

和.m文件:

#import "ConnectionManager.h"
#import "Reachability.h"

@implementation ConnectionManager
@synthesize internetActive, hostActive;

-(id)init {
    self = [super init];
    if(self) {

    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];

    internetReachable = [[Reachability reachabilityForInternetConnection] retain];
    [internetReachable startNotifier];

    hostReachable = [[Reachability reachabilityWithHostName:@"www.apple.com"] retain];
    [hostReachable startNotifier];

    return self;
}

- (void) checkNetworkStatus:(NSNotification *)notice {
    NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
    switch (internetStatus)

    {
        case NotReachable:
        {
            NSLog(@"The internet is down.");
            self.internetActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"The internet is working via WIFI.");
            self.internetActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"The internet is working via WWAN.");
            self.internetActive = YES;

            break;

        }
    }

    NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
    switch (hostStatus)

    {
        case NotReachable:
        {
            NSLog(@"A gateway to the host server is down.");
            self.hostActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"A gateway to the host server is working via WIFI.");
            self.hostActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"A gateway to the host server is working via WWAN.");
            self.hostActive = YES;

            break;

        }
    }

}

// If lower than SDK 5 : Otherwise, remove the observer as pleased.

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}

@end


4

我提取了代码并将其放入一种方法中,希望对其他方法有所帮助。

#import <SystemConfiguration/SystemConfiguration.h>

#import <netinet/in.h>
#import <netinet6/in6.h>

...

- (BOOL)isInternetReachable
{    
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
    SCNetworkReachabilityFlags flags;

    if(reachability == NULL)
        return false;

    if (!(SCNetworkReachabilityGetFlags(reachability, &flags)))
        return false;

    if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        // if target host is not reachable
        return false;


    BOOL isReachable = false;


    if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
    {
        // if target host is reachable and no connection is required
        //  then we'll assume (for now) that your on Wi-Fi
        isReachable = true;
    }


    if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
         (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
    {
        // ... and the connection is on-demand (or on-traffic) if the
        //     calling application is using the CFSocketStream or higher APIs

        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
        {
            // ... and no [user] intervention is needed
            isReachable = true;
        }
    }

    if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
    {
        // ... but WWAN connections are OK if the calling application
        //     is using the CFNetwork (CFSocketStream?) APIs.
        isReachable = true;
    }


    return isReachable;


}

[A]您从哪里提取此代码?您可以发布到源的链接吗?[B]非常感谢您的提取工作!正是我所需要的。我本人要执行这项杂务。
罗勒·布尔克

因此,似乎这是不检查任何远程服务器的唯一答案?
贾斯珀

对于其他在MacOS上进行测试的用户,请注意kSCNetworkReachabilityFlagsIsWWAN适用于OS_IPHONE,而不适用于macOS。
GeoffCoope

4

我写了接受的答案的迅猛版本在这里,柜面如果有人发现它有用,代码编写迅速2,

您可以从SampleCode下载所需的文件

添加Reachability.hReachability.m归档到您的项目中,

现在,Bridging-Header.h如果您的项目不存在,则需要创建文件,

Bridging-Header.h文件内添加以下行:

#import "Reachability.h"

现在为了检查互联网连接

static func isInternetAvailable() -> Bool {
    let networkReachability : Reachability = Reachability.reachabilityForInternetConnection()
    let networkStatus : NetworkStatus = networkReachability.currentReachabilityStatus()

    if networkStatus == NotReachable {
        print("No Internet")
        return false
    } else {
        print("Internet Available")
        return true
    }

}

3

我认为这可能会有所帮助。

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}

如果未更改网络接口并且互联网连接丢失,则此功能将无效。
Pratik Somaiya

2

如果您已经在项目中配置了AFNetworking,也可以尝试使用此方法。

-(void)viewDidLoad{  // -- add connectivity notification --//
[[NSNotificationCenter defaultCenter ] addObserver:self selector:@selector(ReachabilityDidChangeNotification:) name:AFNetworkingReachabilityDidChangeNotification object:nil];}
-(void)ReachabilityDidChangeNotification:(NSNotification *)notify
{
// -- NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));  -- //
NSDictionary *userInfo =[notif userInfo];
AFNetworkReachabilityStatus status= [[userInfo valueForKey:AFNetworkingReachabilityNotificationStatusItem] intValue];
switch (status)
{
    case AFNetworkReachabilityStatusReachableViaWWAN:
    case AFNetworkReachabilityStatusReachableViaWiFi:
        // -- Reachable -- //
// -- Do your stuff when internet connection is available -- //
        [self getLatestStuff];
        NSLog(@"Reachable");
        break;
    case AFNetworkReachabilityStatusNotReachable:
    default:
        // -- Not reachable -- //
        // -- Do your stuff for internet connection not available -- //
NSLog(@"Not Reachable");
        break;
}
}

1

这是一个无需使用可达性即可使用Swift检查连接性的好解决方案。我在这个博客上找到了它。

在名为Network.swift(例如)的项目中创建一个新的Swift文件。将此代码粘贴到该文件中:

import Foundation

public class Network {

    class func isConnectedToNetwork()->Bool{

        var Status:Bool = false
        let url = NSURL(string: "http://google.com/")
        let request = NSMutableURLRequest(URL: url!)
        request.HTTPMethod = "HEAD"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
        request.timeoutInterval = 10.0

        var response: NSURLResponse?

        var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData?

        if let httpResponse = response as? NSHTTPURLResponse {
            if httpResponse.statusCode == 200 {
                Status = true
            }
        }

        return Status
    }
}

然后,您可以使用以下方法检查项目中任何地方的连通性:

if Network.isConnectedToNetwork() == true {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
}

0

编辑:这将不适用于网络URL(请参阅评论)

从iOS 5开始,有一个新的NSURL实例方法:

- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error

将其指向您关注的网站或指向apple.com;我认为这是一个新的单线呼叫,以查看您的设备是否正在运行互联网。


实际上,在我自己的测试中,这总是返回false。文档指出这是4.x的情况,但在5+版本中应该可以使用。YMMV
SG1

这似乎不适用于您正在寻找的情况。见stackoverflow.com/questions/9266154/...
弥Hainline

7
这用于文件URL,而不是Internet URL。
维克多·博格丹

0

我对可用的Internet检查选项也不满意(为什么这不是本地API?!?!)

我自己的问题是100%的数据包丢失-当设备连接到路由器但路由器未连接到Internet时。可达性和其他将久存。我创建了一个实用程序单例类,以通过添加异步超时来解决该问题。它在我的应用程序中工作正常。希望能帮助到你。这是github上的链接:

https://github.com/fareast555/TFInternetChecker


0

在(iOS)Xcode 8.2和Swift 3.0中检查Internet连接可用性

这是检查网络可用性的简单方法。我设法将其翻译为Swift 2.0,并在这里完成了最终代码。现有的Apple Reachability类和其他第三方库似乎过于复杂,无法转换为Swift。

这适用于3G和WiFi连接。

不要忘记将“ SystemConfiguration.framework”添加到项目构建器。

//Create new swift class file Reachability in your project.

import SystemConfiguration

public class Reachability {
class func isConnectedToNetwork() -> Bool {
    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)
    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
        }
    }
    var flags = SCNetworkReachabilityFlags()
    if !SCNetworkReachabilityGetFlags(defaultRouteReachability! , &flags) {
        return false
    }
    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    return (isReachable && !needsConnection)
   }
}

// Check network connectivity from anywhere in project by using this code.

if Reachability.isConnectedToNetwork() == true {
     print("Internet connection OK")
} else {
 print("Internet connection FAILED")
}

随着版本的Xcode 7.2(7C68),该代码有编译器错误
丹尼尔

@Daniel,感谢您指出问题,这是因为指针不安全,因为Swift 2 / Xcode正在更新那里的语法,我们应该遵循它们。我已经更新了相同的代码。ty;)
ViJay Avhad 2016年

如果您已连接到Wi-Fi,但无法连接到互联网(打开Goog​​le页面),则仍会返回true。这是不对的。
Arildo Junior '02

0

受同名亿万启发,在Swift中用闭包重写了Apple可达性的替代品:https : //github.com/ashleymills/Reachability.swift

  1. 将文件拖放Reachability.swift到您的项目中。或者,使用CocoaPodsCarthage-请参阅项目自述文件的“ 安装”部分。

  2. 获取有关网络连接的通知:

    //declare this property where it won't go out of scope relative to your listener
    let reachability = Reachability()!
    
    reachability.whenReachable = { reachability in
        if reachability.isReachableViaWiFi {
            print("Reachable via WiFi")
        } else {
            print("Reachable via Cellular")
        }
    }
    
    reachability.whenUnreachable = { _ in
        print("Not reachable")
    }
    
    do {
        try reachability.startNotifier()
    } catch {
        print("Unable to start notifier")
    }

    并用于停止通知

    reachability.stopNotifier()

0

Alamofire

如果您已经将Alamofire用于所有RESTful Api,则可以从中受益

您可以将以下类添加到您的应用中,并调用MNNetworkUtils.main.isConnected()以获取有关其是否已连接的布尔值。

#import Alamofire

class MNNetworkUtils {
  static let main = MNNetworkUtils()
  init() {
    manager = NetworkReachabilityManager(host: "google.com")
    listenForReachability()
  }

  private let manager: NetworkReachabilityManager?
  private var reachable: Bool = false
  private func listenForReachability() {
    self.manager?.listener = { [unowned self] status in
      switch status {
      case .notReachable:
        self.reachable = false
      case .reachable(_), .unknown:
        self.reachable = true
      }
    }
    self.manager?.startListening()
  }

  func isConnected() -> Bool {
    return reachable
  }
}

这是一个单例课程。每次用户连接或断开网络时,它都会self.reachable正确覆盖true / false,因为我们开始侦听NetworkReachabilityManageron单例初始化。

另外,为了监视可达性,您需要提供一个主机,目前我正在google.com随意更改为任何其他主机,或者如果需要,可以更改为其中一个。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.